Roo/bootstrap/ComboBox.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372     }
373 });
374
375  /*
376  * - 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  * {Boolean} showArrow show arrow next to the text (default true)
4573  * @constructor
4574  * Create a new Navbar Button
4575  * @param {Object} config The config object
4576  */
4577 Roo.bootstrap.NavSidebarItem = function(config){
4578     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4579     this.addEvents({
4580         // raw events
4581         /**
4582          * @event click
4583          * The raw click event for the entire grid.
4584          * @param {Roo.EventObject} e
4585          */
4586         "click" : true,
4587          /**
4588             * @event changed
4589             * Fires when the active item active state changes
4590             * @param {Roo.bootstrap.NavSidebarItem} this
4591             * @param {boolean} state the new state
4592              
4593          */
4594         'changed': true
4595     });
4596    
4597 };
4598
4599 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4600     
4601     badgeWeight : 'default',
4602     
4603     open: false,
4604     
4605     buttonView : false,
4606     
4607     buttonWeight : 'default',
4608     
4609     buttonSize : 'md',
4610     
4611     showArrow : true,
4612     
4613     getAutoCreate : function(){
4614         
4615         
4616         var a = {
4617                 tag: 'a',
4618                 href : this.href || '#',
4619                 cls: '',
4620                 html : '',
4621                 cn : []
4622         };
4623         
4624         if(this.buttonView){
4625             a = {
4626                 tag: 'button',
4627                 href : this.href || '#',
4628                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4629                 html : this.html,
4630                 cn : []
4631             };
4632         }
4633         
4634         var cfg = {
4635             tag: 'li',
4636             cls: '',
4637             cn: [ a ]
4638         };
4639         
4640         if (this.active) {
4641             cfg.cls += ' active';
4642         }
4643         
4644         if (this.disabled) {
4645             cfg.cls += ' disabled';
4646         }
4647         if (this.open) {
4648             cfg.cls += ' open x-open';
4649         }
4650         // left icon..
4651         if (this.glyphicon || this.icon) {
4652             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4653             a.cn.push({ tag : 'i', cls : c }) ;
4654         }
4655         
4656         if(!this.buttonView){
4657             var span = {
4658                 tag: 'span',
4659                 html : this.html || ''
4660             };
4661
4662             a.cn.push(span);
4663             
4664         }
4665         
4666         if (this.badge !== '') {
4667             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4668         }
4669         
4670         if (this.menu) {
4671             
4672             if(this.showArrow){
4673                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4674             }
4675             
4676             a.cls += ' dropdown-toggle treeview' ;
4677         }
4678         
4679         return cfg;
4680     },
4681     
4682     initEvents : function()
4683     { 
4684         if (typeof (this.menu) != 'undefined') {
4685             this.menu.parentType = this.xtype;
4686             this.menu.triggerEl = this.el;
4687             this.menu = this.addxtype(Roo.apply({}, this.menu));
4688         }
4689         
4690         this.el.on('click', this.onClick, this);
4691         
4692         if(this.badge !== ''){
4693             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4694         }
4695         
4696     },
4697     
4698     onClick : function(e)
4699     {
4700         if(this.disabled){
4701             e.preventDefault();
4702             return;
4703         }
4704         
4705         if(this.preventDefault){
4706             e.preventDefault();
4707         }
4708         
4709         this.fireEvent('click', this);
4710     },
4711     
4712     disable : function()
4713     {
4714         this.setDisabled(true);
4715     },
4716     
4717     enable : function()
4718     {
4719         this.setDisabled(false);
4720     },
4721     
4722     setDisabled : function(state)
4723     {
4724         if(this.disabled == state){
4725             return;
4726         }
4727         
4728         this.disabled = state;
4729         
4730         if (state) {
4731             this.el.addClass('disabled');
4732             return;
4733         }
4734         
4735         this.el.removeClass('disabled');
4736         
4737         return;
4738     },
4739     
4740     setActive : function(state)
4741     {
4742         if(this.active == state){
4743             return;
4744         }
4745         
4746         this.active = state;
4747         
4748         if (state) {
4749             this.el.addClass('active');
4750             return;
4751         }
4752         
4753         this.el.removeClass('active');
4754         
4755         return;
4756     },
4757     
4758     isActive: function () 
4759     {
4760         return this.active;
4761     },
4762     
4763     setBadge : function(str)
4764     {
4765         if(!this.badgeEl){
4766             return;
4767         }
4768         
4769         this.badgeEl.dom.innerHTML = str;
4770     }
4771     
4772    
4773      
4774  
4775 });
4776  
4777
4778  /*
4779  * - LGPL
4780  *
4781  * row
4782  * 
4783  */
4784
4785 /**
4786  * @class Roo.bootstrap.Row
4787  * @extends Roo.bootstrap.Component
4788  * Bootstrap Row class (contains columns...)
4789  * 
4790  * @constructor
4791  * Create a new Row
4792  * @param {Object} config The config object
4793  */
4794
4795 Roo.bootstrap.Row = function(config){
4796     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4797 };
4798
4799 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4800     
4801     getAutoCreate : function(){
4802        return {
4803             cls: 'row clearfix'
4804        };
4805     }
4806     
4807     
4808 });
4809
4810  
4811
4812  /*
4813  * - LGPL
4814  *
4815  * element
4816  * 
4817  */
4818
4819 /**
4820  * @class Roo.bootstrap.Element
4821  * @extends Roo.bootstrap.Component
4822  * Bootstrap Element class
4823  * @cfg {String} html contents of the element
4824  * @cfg {String} tag tag of the element
4825  * @cfg {String} cls class of the element
4826  * @cfg {Boolean} preventDefault (true|false) default false
4827  * @cfg {Boolean} clickable (true|false) default false
4828  * 
4829  * @constructor
4830  * Create a new Element
4831  * @param {Object} config The config object
4832  */
4833
4834 Roo.bootstrap.Element = function(config){
4835     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4836     
4837     this.addEvents({
4838         // raw events
4839         /**
4840          * @event click
4841          * When a element is chick
4842          * @param {Roo.bootstrap.Element} this
4843          * @param {Roo.EventObject} e
4844          */
4845         "click" : true
4846     });
4847 };
4848
4849 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4850     
4851     tag: 'div',
4852     cls: '',
4853     html: '',
4854     preventDefault: false, 
4855     clickable: false,
4856     
4857     getAutoCreate : function(){
4858         
4859         var cfg = {
4860             tag: this.tag,
4861             cls: this.cls,
4862             html: this.html
4863         };
4864         
4865         return cfg;
4866     },
4867     
4868     initEvents: function() 
4869     {
4870         Roo.bootstrap.Element.superclass.initEvents.call(this);
4871         
4872         if(this.clickable){
4873             this.el.on('click', this.onClick, this);
4874         }
4875         
4876     },
4877     
4878     onClick : function(e)
4879     {
4880         if(this.preventDefault){
4881             e.preventDefault();
4882         }
4883         
4884         this.fireEvent('click', this, e);
4885     },
4886     
4887     getValue : function()
4888     {
4889         return this.el.dom.innerHTML;
4890     },
4891     
4892     setValue : function(value)
4893     {
4894         this.el.dom.innerHTML = value;
4895     }
4896    
4897 });
4898
4899  
4900
4901  /*
4902  * - LGPL
4903  *
4904  * pagination
4905  * 
4906  */
4907
4908 /**
4909  * @class Roo.bootstrap.Pagination
4910  * @extends Roo.bootstrap.Component
4911  * Bootstrap Pagination class
4912  * @cfg {String} size xs | sm | md | lg
4913  * @cfg {Boolean} inverse false | true
4914  * 
4915  * @constructor
4916  * Create a new Pagination
4917  * @param {Object} config The config object
4918  */
4919
4920 Roo.bootstrap.Pagination = function(config){
4921     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4922 };
4923
4924 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4925     
4926     cls: false,
4927     size: false,
4928     inverse: false,
4929     
4930     getAutoCreate : function(){
4931         var cfg = {
4932             tag: 'ul',
4933                 cls: 'pagination'
4934         };
4935         if (this.inverse) {
4936             cfg.cls += ' inverse';
4937         }
4938         if (this.html) {
4939             cfg.html=this.html;
4940         }
4941         if (this.cls) {
4942             cfg.cls += " " + this.cls;
4943         }
4944         return cfg;
4945     }
4946    
4947 });
4948
4949  
4950
4951  /*
4952  * - LGPL
4953  *
4954  * Pagination item
4955  * 
4956  */
4957
4958
4959 /**
4960  * @class Roo.bootstrap.PaginationItem
4961  * @extends Roo.bootstrap.Component
4962  * Bootstrap PaginationItem class
4963  * @cfg {String} html text
4964  * @cfg {String} href the link
4965  * @cfg {Boolean} preventDefault (true | false) default true
4966  * @cfg {Boolean} active (true | false) default false
4967  * @cfg {Boolean} disabled default false
4968  * 
4969  * 
4970  * @constructor
4971  * Create a new PaginationItem
4972  * @param {Object} config The config object
4973  */
4974
4975
4976 Roo.bootstrap.PaginationItem = function(config){
4977     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4978     this.addEvents({
4979         // raw events
4980         /**
4981          * @event click
4982          * The raw click event for the entire grid.
4983          * @param {Roo.EventObject} e
4984          */
4985         "click" : true
4986     });
4987 };
4988
4989 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4990     
4991     href : false,
4992     html : false,
4993     preventDefault: true,
4994     active : false,
4995     cls : false,
4996     disabled: false,
4997     
4998     getAutoCreate : function(){
4999         var cfg= {
5000             tag: 'li',
5001             cn: [
5002                 {
5003                     tag : 'a',
5004                     href : this.href ? this.href : '#',
5005                     html : this.html ? this.html : ''
5006                 }
5007             ]
5008         };
5009         
5010         if(this.cls){
5011             cfg.cls = this.cls;
5012         }
5013         
5014         if(this.disabled){
5015             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5016         }
5017         
5018         if(this.active){
5019             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5020         }
5021         
5022         return cfg;
5023     },
5024     
5025     initEvents: function() {
5026         
5027         this.el.on('click', this.onClick, this);
5028         
5029     },
5030     onClick : function(e)
5031     {
5032         Roo.log('PaginationItem on click ');
5033         if(this.preventDefault){
5034             e.preventDefault();
5035         }
5036         
5037         if(this.disabled){
5038             return;
5039         }
5040         
5041         this.fireEvent('click', this, e);
5042     }
5043    
5044 });
5045
5046  
5047
5048  /*
5049  * - LGPL
5050  *
5051  * slider
5052  * 
5053  */
5054
5055
5056 /**
5057  * @class Roo.bootstrap.Slider
5058  * @extends Roo.bootstrap.Component
5059  * Bootstrap Slider class
5060  *    
5061  * @constructor
5062  * Create a new Slider
5063  * @param {Object} config The config object
5064  */
5065
5066 Roo.bootstrap.Slider = function(config){
5067     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5068 };
5069
5070 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5071     
5072     getAutoCreate : function(){
5073         
5074         var cfg = {
5075             tag: 'div',
5076             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5077             cn: [
5078                 {
5079                     tag: 'a',
5080                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5081                 }
5082             ]
5083         };
5084         
5085         return cfg;
5086     }
5087    
5088 });
5089
5090  /*
5091  * Based on:
5092  * Ext JS Library 1.1.1
5093  * Copyright(c) 2006-2007, Ext JS, LLC.
5094  *
5095  * Originally Released Under LGPL - original licence link has changed is not relivant.
5096  *
5097  * Fork - LGPL
5098  * <script type="text/javascript">
5099  */
5100  
5101
5102 /**
5103  * @class Roo.grid.ColumnModel
5104  * @extends Roo.util.Observable
5105  * This is the default implementation of a ColumnModel used by the Grid. It defines
5106  * the columns in the grid.
5107  * <br>Usage:<br>
5108  <pre><code>
5109  var colModel = new Roo.grid.ColumnModel([
5110         {header: "Ticker", width: 60, sortable: true, locked: true},
5111         {header: "Company Name", width: 150, sortable: true},
5112         {header: "Market Cap.", width: 100, sortable: true},
5113         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5114         {header: "Employees", width: 100, sortable: true, resizable: false}
5115  ]);
5116  </code></pre>
5117  * <p>
5118  
5119  * The config options listed for this class are options which may appear in each
5120  * individual column definition.
5121  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5122  * @constructor
5123  * @param {Object} config An Array of column config objects. See this class's
5124  * config objects for details.
5125 */
5126 Roo.grid.ColumnModel = function(config){
5127         /**
5128      * The config passed into the constructor
5129      */
5130     this.config = config;
5131     this.lookup = {};
5132
5133     // if no id, create one
5134     // if the column does not have a dataIndex mapping,
5135     // map it to the order it is in the config
5136     for(var i = 0, len = config.length; i < len; i++){
5137         var c = config[i];
5138         if(typeof c.dataIndex == "undefined"){
5139             c.dataIndex = i;
5140         }
5141         if(typeof c.renderer == "string"){
5142             c.renderer = Roo.util.Format[c.renderer];
5143         }
5144         if(typeof c.id == "undefined"){
5145             c.id = Roo.id();
5146         }
5147         if(c.editor && c.editor.xtype){
5148             c.editor  = Roo.factory(c.editor, Roo.grid);
5149         }
5150         if(c.editor && c.editor.isFormField){
5151             c.editor = new Roo.grid.GridEditor(c.editor);
5152         }
5153         this.lookup[c.id] = c;
5154     }
5155
5156     /**
5157      * The width of columns which have no width specified (defaults to 100)
5158      * @type Number
5159      */
5160     this.defaultWidth = 100;
5161
5162     /**
5163      * Default sortable of columns which have no sortable specified (defaults to false)
5164      * @type Boolean
5165      */
5166     this.defaultSortable = false;
5167
5168     this.addEvents({
5169         /**
5170              * @event widthchange
5171              * Fires when the width of a column changes.
5172              * @param {ColumnModel} this
5173              * @param {Number} columnIndex The column index
5174              * @param {Number} newWidth The new width
5175              */
5176             "widthchange": true,
5177         /**
5178              * @event headerchange
5179              * Fires when the text of a header changes.
5180              * @param {ColumnModel} this
5181              * @param {Number} columnIndex The column index
5182              * @param {Number} newText The new header text
5183              */
5184             "headerchange": true,
5185         /**
5186              * @event hiddenchange
5187              * Fires when a column is hidden or "unhidden".
5188              * @param {ColumnModel} this
5189              * @param {Number} columnIndex The column index
5190              * @param {Boolean} hidden true if hidden, false otherwise
5191              */
5192             "hiddenchange": true,
5193             /**
5194          * @event columnmoved
5195          * Fires when a column is moved.
5196          * @param {ColumnModel} this
5197          * @param {Number} oldIndex
5198          * @param {Number} newIndex
5199          */
5200         "columnmoved" : true,
5201         /**
5202          * @event columlockchange
5203          * Fires when a column's locked state is changed
5204          * @param {ColumnModel} this
5205          * @param {Number} colIndex
5206          * @param {Boolean} locked true if locked
5207          */
5208         "columnlockchange" : true
5209     });
5210     Roo.grid.ColumnModel.superclass.constructor.call(this);
5211 };
5212 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5213     /**
5214      * @cfg {String} header The header text to display in the Grid view.
5215      */
5216     /**
5217      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5218      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5219      * specified, the column's index is used as an index into the Record's data Array.
5220      */
5221     /**
5222      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5223      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5224      */
5225     /**
5226      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5227      * Defaults to the value of the {@link #defaultSortable} property.
5228      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5229      */
5230     /**
5231      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5232      */
5233     /**
5234      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5235      */
5236     /**
5237      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5238      */
5239     /**
5240      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5241      */
5242     /**
5243      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5244      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5245      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5246      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5247      */
5248        /**
5249      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5250      */
5251     /**
5252      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5253      */
5254     /**
5255      * @cfg {String} cursor (Optional)
5256      */
5257     /**
5258      * @cfg {String} tooltip (Optional)
5259      */
5260     /**
5261      * @cfg {Number} xs (Optional)
5262      */
5263     /**
5264      * @cfg {Number} sm (Optional)
5265      */
5266     /**
5267      * @cfg {Number} md (Optional)
5268      */
5269     /**
5270      * @cfg {Number} lg (Optional)
5271      */
5272     /**
5273      * Returns the id of the column at the specified index.
5274      * @param {Number} index The column index
5275      * @return {String} the id
5276      */
5277     getColumnId : function(index){
5278         return this.config[index].id;
5279     },
5280
5281     /**
5282      * Returns the column for a specified id.
5283      * @param {String} id The column id
5284      * @return {Object} the column
5285      */
5286     getColumnById : function(id){
5287         return this.lookup[id];
5288     },
5289
5290     
5291     /**
5292      * Returns the column for a specified dataIndex.
5293      * @param {String} dataIndex The column dataIndex
5294      * @return {Object|Boolean} the column or false if not found
5295      */
5296     getColumnByDataIndex: function(dataIndex){
5297         var index = this.findColumnIndex(dataIndex);
5298         return index > -1 ? this.config[index] : false;
5299     },
5300     
5301     /**
5302      * Returns the index for a specified column id.
5303      * @param {String} id The column id
5304      * @return {Number} the index, or -1 if not found
5305      */
5306     getIndexById : function(id){
5307         for(var i = 0, len = this.config.length; i < len; i++){
5308             if(this.config[i].id == id){
5309                 return i;
5310             }
5311         }
5312         return -1;
5313     },
5314     
5315     /**
5316      * Returns the index for a specified column dataIndex.
5317      * @param {String} dataIndex The column dataIndex
5318      * @return {Number} the index, or -1 if not found
5319      */
5320     
5321     findColumnIndex : function(dataIndex){
5322         for(var i = 0, len = this.config.length; i < len; i++){
5323             if(this.config[i].dataIndex == dataIndex){
5324                 return i;
5325             }
5326         }
5327         return -1;
5328     },
5329     
5330     
5331     moveColumn : function(oldIndex, newIndex){
5332         var c = this.config[oldIndex];
5333         this.config.splice(oldIndex, 1);
5334         this.config.splice(newIndex, 0, c);
5335         this.dataMap = null;
5336         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5337     },
5338
5339     isLocked : function(colIndex){
5340         return this.config[colIndex].locked === true;
5341     },
5342
5343     setLocked : function(colIndex, value, suppressEvent){
5344         if(this.isLocked(colIndex) == value){
5345             return;
5346         }
5347         this.config[colIndex].locked = value;
5348         if(!suppressEvent){
5349             this.fireEvent("columnlockchange", this, colIndex, value);
5350         }
5351     },
5352
5353     getTotalLockedWidth : function(){
5354         var totalWidth = 0;
5355         for(var i = 0; i < this.config.length; i++){
5356             if(this.isLocked(i) && !this.isHidden(i)){
5357                 this.totalWidth += this.getColumnWidth(i);
5358             }
5359         }
5360         return totalWidth;
5361     },
5362
5363     getLockedCount : function(){
5364         for(var i = 0, len = this.config.length; i < len; i++){
5365             if(!this.isLocked(i)){
5366                 return i;
5367             }
5368         }
5369         
5370         return this.config.length;
5371     },
5372
5373     /**
5374      * Returns the number of columns.
5375      * @return {Number}
5376      */
5377     getColumnCount : function(visibleOnly){
5378         if(visibleOnly === true){
5379             var c = 0;
5380             for(var i = 0, len = this.config.length; i < len; i++){
5381                 if(!this.isHidden(i)){
5382                     c++;
5383                 }
5384             }
5385             return c;
5386         }
5387         return this.config.length;
5388     },
5389
5390     /**
5391      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5392      * @param {Function} fn
5393      * @param {Object} scope (optional)
5394      * @return {Array} result
5395      */
5396     getColumnsBy : function(fn, scope){
5397         var r = [];
5398         for(var i = 0, len = this.config.length; i < len; i++){
5399             var c = this.config[i];
5400             if(fn.call(scope||this, c, i) === true){
5401                 r[r.length] = c;
5402             }
5403         }
5404         return r;
5405     },
5406
5407     /**
5408      * Returns true if the specified column is sortable.
5409      * @param {Number} col The column index
5410      * @return {Boolean}
5411      */
5412     isSortable : function(col){
5413         if(typeof this.config[col].sortable == "undefined"){
5414             return this.defaultSortable;
5415         }
5416         return this.config[col].sortable;
5417     },
5418
5419     /**
5420      * Returns the rendering (formatting) function defined for the column.
5421      * @param {Number} col The column index.
5422      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5423      */
5424     getRenderer : function(col){
5425         if(!this.config[col].renderer){
5426             return Roo.grid.ColumnModel.defaultRenderer;
5427         }
5428         return this.config[col].renderer;
5429     },
5430
5431     /**
5432      * Sets the rendering (formatting) function for a column.
5433      * @param {Number} col The column index
5434      * @param {Function} fn The function to use to process the cell's raw data
5435      * to return HTML markup for the grid view. The render function is called with
5436      * the following parameters:<ul>
5437      * <li>Data value.</li>
5438      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5439      * <li>css A CSS style string to apply to the table cell.</li>
5440      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5441      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5442      * <li>Row index</li>
5443      * <li>Column index</li>
5444      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5445      */
5446     setRenderer : function(col, fn){
5447         this.config[col].renderer = fn;
5448     },
5449
5450     /**
5451      * Returns the width for the specified column.
5452      * @param {Number} col The column index
5453      * @return {Number}
5454      */
5455     getColumnWidth : function(col){
5456         return this.config[col].width * 1 || this.defaultWidth;
5457     },
5458
5459     /**
5460      * Sets the width for a column.
5461      * @param {Number} col The column index
5462      * @param {Number} width The new width
5463      */
5464     setColumnWidth : function(col, width, suppressEvent){
5465         this.config[col].width = width;
5466         this.totalWidth = null;
5467         if(!suppressEvent){
5468              this.fireEvent("widthchange", this, col, width);
5469         }
5470     },
5471
5472     /**
5473      * Returns the total width of all columns.
5474      * @param {Boolean} includeHidden True to include hidden column widths
5475      * @return {Number}
5476      */
5477     getTotalWidth : function(includeHidden){
5478         if(!this.totalWidth){
5479             this.totalWidth = 0;
5480             for(var i = 0, len = this.config.length; i < len; i++){
5481                 if(includeHidden || !this.isHidden(i)){
5482                     this.totalWidth += this.getColumnWidth(i);
5483                 }
5484             }
5485         }
5486         return this.totalWidth;
5487     },
5488
5489     /**
5490      * Returns the header for the specified column.
5491      * @param {Number} col The column index
5492      * @return {String}
5493      */
5494     getColumnHeader : function(col){
5495         return this.config[col].header;
5496     },
5497
5498     /**
5499      * Sets the header for a column.
5500      * @param {Number} col The column index
5501      * @param {String} header The new header
5502      */
5503     setColumnHeader : function(col, header){
5504         this.config[col].header = header;
5505         this.fireEvent("headerchange", this, col, header);
5506     },
5507
5508     /**
5509      * Returns the tooltip for the specified column.
5510      * @param {Number} col The column index
5511      * @return {String}
5512      */
5513     getColumnTooltip : function(col){
5514             return this.config[col].tooltip;
5515     },
5516     /**
5517      * Sets the tooltip for a column.
5518      * @param {Number} col The column index
5519      * @param {String} tooltip The new tooltip
5520      */
5521     setColumnTooltip : function(col, tooltip){
5522             this.config[col].tooltip = tooltip;
5523     },
5524
5525     /**
5526      * Returns the dataIndex for the specified column.
5527      * @param {Number} col The column index
5528      * @return {Number}
5529      */
5530     getDataIndex : function(col){
5531         return this.config[col].dataIndex;
5532     },
5533
5534     /**
5535      * Sets the dataIndex for a column.
5536      * @param {Number} col The column index
5537      * @param {Number} dataIndex The new dataIndex
5538      */
5539     setDataIndex : function(col, dataIndex){
5540         this.config[col].dataIndex = dataIndex;
5541     },
5542
5543     
5544     
5545     /**
5546      * Returns true if the cell is editable.
5547      * @param {Number} colIndex The column index
5548      * @param {Number} rowIndex The row index - this is nto actually used..?
5549      * @return {Boolean}
5550      */
5551     isCellEditable : function(colIndex, rowIndex){
5552         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5553     },
5554
5555     /**
5556      * Returns the editor defined for the cell/column.
5557      * return false or null to disable editing.
5558      * @param {Number} colIndex The column index
5559      * @param {Number} rowIndex The row index
5560      * @return {Object}
5561      */
5562     getCellEditor : function(colIndex, rowIndex){
5563         return this.config[colIndex].editor;
5564     },
5565
5566     /**
5567      * Sets if a column is editable.
5568      * @param {Number} col The column index
5569      * @param {Boolean} editable True if the column is editable
5570      */
5571     setEditable : function(col, editable){
5572         this.config[col].editable = editable;
5573     },
5574
5575
5576     /**
5577      * Returns true if the column is hidden.
5578      * @param {Number} colIndex The column index
5579      * @return {Boolean}
5580      */
5581     isHidden : function(colIndex){
5582         return this.config[colIndex].hidden;
5583     },
5584
5585
5586     /**
5587      * Returns true if the column width cannot be changed
5588      */
5589     isFixed : function(colIndex){
5590         return this.config[colIndex].fixed;
5591     },
5592
5593     /**
5594      * Returns true if the column can be resized
5595      * @return {Boolean}
5596      */
5597     isResizable : function(colIndex){
5598         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5599     },
5600     /**
5601      * Sets if a column is hidden.
5602      * @param {Number} colIndex The column index
5603      * @param {Boolean} hidden True if the column is hidden
5604      */
5605     setHidden : function(colIndex, hidden){
5606         this.config[colIndex].hidden = hidden;
5607         this.totalWidth = null;
5608         this.fireEvent("hiddenchange", this, colIndex, hidden);
5609     },
5610
5611     /**
5612      * Sets the editor for a column.
5613      * @param {Number} col The column index
5614      * @param {Object} editor The editor object
5615      */
5616     setEditor : function(col, editor){
5617         this.config[col].editor = editor;
5618     }
5619 });
5620
5621 Roo.grid.ColumnModel.defaultRenderer = function(value)
5622 {
5623     if(typeof value == "object") {
5624         return value;
5625     }
5626         if(typeof value == "string" && value.length < 1){
5627             return "&#160;";
5628         }
5629     
5630         return String.format("{0}", value);
5631 };
5632
5633 // Alias for backwards compatibility
5634 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5635 /*
5636  * Based on:
5637  * Ext JS Library 1.1.1
5638  * Copyright(c) 2006-2007, Ext JS, LLC.
5639  *
5640  * Originally Released Under LGPL - original licence link has changed is not relivant.
5641  *
5642  * Fork - LGPL
5643  * <script type="text/javascript">
5644  */
5645  
5646 /**
5647  * @class Roo.LoadMask
5648  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5649  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5650  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5651  * element's UpdateManager load indicator and will be destroyed after the initial load.
5652  * @constructor
5653  * Create a new LoadMask
5654  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5655  * @param {Object} config The config object
5656  */
5657 Roo.LoadMask = function(el, config){
5658     this.el = Roo.get(el);
5659     Roo.apply(this, config);
5660     if(this.store){
5661         this.store.on('beforeload', this.onBeforeLoad, this);
5662         this.store.on('load', this.onLoad, this);
5663         this.store.on('loadexception', this.onLoadException, this);
5664         this.removeMask = false;
5665     }else{
5666         var um = this.el.getUpdateManager();
5667         um.showLoadIndicator = false; // disable the default indicator
5668         um.on('beforeupdate', this.onBeforeLoad, this);
5669         um.on('update', this.onLoad, this);
5670         um.on('failure', this.onLoad, this);
5671         this.removeMask = true;
5672     }
5673 };
5674
5675 Roo.LoadMask.prototype = {
5676     /**
5677      * @cfg {Boolean} removeMask
5678      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5679      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5680      */
5681     /**
5682      * @cfg {String} msg
5683      * The text to display in a centered loading message box (defaults to 'Loading...')
5684      */
5685     msg : 'Loading...',
5686     /**
5687      * @cfg {String} msgCls
5688      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5689      */
5690     msgCls : 'x-mask-loading',
5691
5692     /**
5693      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5694      * @type Boolean
5695      */
5696     disabled: false,
5697
5698     /**
5699      * Disables the mask to prevent it from being displayed
5700      */
5701     disable : function(){
5702        this.disabled = true;
5703     },
5704
5705     /**
5706      * Enables the mask so that it can be displayed
5707      */
5708     enable : function(){
5709         this.disabled = false;
5710     },
5711     
5712     onLoadException : function()
5713     {
5714         Roo.log(arguments);
5715         
5716         if (typeof(arguments[3]) != 'undefined') {
5717             Roo.MessageBox.alert("Error loading",arguments[3]);
5718         } 
5719         /*
5720         try {
5721             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5722                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5723             }   
5724         } catch(e) {
5725             
5726         }
5727         */
5728     
5729         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5730     },
5731     // private
5732     onLoad : function()
5733     {
5734         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5735     },
5736
5737     // private
5738     onBeforeLoad : function(){
5739         if(!this.disabled){
5740             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5741         }
5742     },
5743
5744     // private
5745     destroy : function(){
5746         if(this.store){
5747             this.store.un('beforeload', this.onBeforeLoad, this);
5748             this.store.un('load', this.onLoad, this);
5749             this.store.un('loadexception', this.onLoadException, this);
5750         }else{
5751             var um = this.el.getUpdateManager();
5752             um.un('beforeupdate', this.onBeforeLoad, this);
5753             um.un('update', this.onLoad, this);
5754             um.un('failure', this.onLoad, this);
5755         }
5756     }
5757 };/*
5758  * - LGPL
5759  *
5760  * table
5761  * 
5762  */
5763
5764 /**
5765  * @class Roo.bootstrap.Table
5766  * @extends Roo.bootstrap.Component
5767  * Bootstrap Table class
5768  * @cfg {String} cls table class
5769  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5770  * @cfg {String} bgcolor Specifies the background color for a table
5771  * @cfg {Number} border Specifies whether the table cells should have borders or not
5772  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5773  * @cfg {Number} cellspacing Specifies the space between cells
5774  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5775  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5776  * @cfg {String} sortable Specifies that the table should be sortable
5777  * @cfg {String} summary Specifies a summary of the content of a table
5778  * @cfg {Number} width Specifies the width of a table
5779  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5780  * 
5781  * @cfg {boolean} striped Should the rows be alternative striped
5782  * @cfg {boolean} bordered Add borders to the table
5783  * @cfg {boolean} hover Add hover highlighting
5784  * @cfg {boolean} condensed Format condensed
5785  * @cfg {boolean} responsive Format condensed
5786  * @cfg {Boolean} loadMask (true|false) default false
5787  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5788  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5789  * @cfg {Boolean} rowSelection (true|false) default false
5790  * @cfg {Boolean} cellSelection (true|false) default false
5791  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5792  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5793  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5794  
5795  * 
5796  * @constructor
5797  * Create a new Table
5798  * @param {Object} config The config object
5799  */
5800
5801 Roo.bootstrap.Table = function(config){
5802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5803     
5804   
5805     
5806     // BC...
5807     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5808     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5809     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5810     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5811     
5812     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5813     if (this.sm) {
5814         this.sm.grid = this;
5815         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5816         this.sm = this.selModel;
5817         this.sm.xmodule = this.xmodule || false;
5818     }
5819     
5820     if (this.cm && typeof(this.cm.config) == 'undefined') {
5821         this.colModel = new Roo.grid.ColumnModel(this.cm);
5822         this.cm = this.colModel;
5823         this.cm.xmodule = this.xmodule || false;
5824     }
5825     if (this.store) {
5826         this.store= Roo.factory(this.store, Roo.data);
5827         this.ds = this.store;
5828         this.ds.xmodule = this.xmodule || false;
5829          
5830     }
5831     if (this.footer && this.store) {
5832         this.footer.dataSource = this.ds;
5833         this.footer = Roo.factory(this.footer);
5834     }
5835     
5836     /** @private */
5837     this.addEvents({
5838         /**
5839          * @event cellclick
5840          * Fires when a cell is clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Roo.Element} el
5843          * @param {Number} rowIndex
5844          * @param {Number} columnIndex
5845          * @param {Roo.EventObject} e
5846          */
5847         "cellclick" : true,
5848         /**
5849          * @event celldblclick
5850          * Fires when a cell is double clicked
5851          * @param {Roo.bootstrap.Table} this
5852          * @param {Roo.Element} el
5853          * @param {Number} rowIndex
5854          * @param {Number} columnIndex
5855          * @param {Roo.EventObject} e
5856          */
5857         "celldblclick" : true,
5858         /**
5859          * @event rowclick
5860          * Fires when a row is clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Roo.Element} el
5863          * @param {Number} rowIndex
5864          * @param {Roo.EventObject} e
5865          */
5866         "rowclick" : true,
5867         /**
5868          * @event rowdblclick
5869          * Fires when a row is double clicked
5870          * @param {Roo.bootstrap.Table} this
5871          * @param {Roo.Element} el
5872          * @param {Number} rowIndex
5873          * @param {Roo.EventObject} e
5874          */
5875         "rowdblclick" : true,
5876         /**
5877          * @event mouseover
5878          * Fires when a mouseover occur
5879          * @param {Roo.bootstrap.Table} this
5880          * @param {Roo.Element} el
5881          * @param {Number} rowIndex
5882          * @param {Number} columnIndex
5883          * @param {Roo.EventObject} e
5884          */
5885         "mouseover" : true,
5886         /**
5887          * @event mouseout
5888          * Fires when a mouseout occur
5889          * @param {Roo.bootstrap.Table} this
5890          * @param {Roo.Element} el
5891          * @param {Number} rowIndex
5892          * @param {Number} columnIndex
5893          * @param {Roo.EventObject} e
5894          */
5895         "mouseout" : true,
5896         /**
5897          * @event rowclass
5898          * Fires when a row is rendered, so you can change add a style to it.
5899          * @param {Roo.bootstrap.Table} this
5900          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5901          */
5902         'rowclass' : true,
5903           /**
5904          * @event rowsrendered
5905          * Fires when all the  rows have been rendered
5906          * @param {Roo.bootstrap.Table} this
5907          */
5908         'rowsrendered' : true,
5909         /**
5910          * @event contextmenu
5911          * The raw contextmenu event for the entire grid.
5912          * @param {Roo.EventObject} e
5913          */
5914         "contextmenu" : true,
5915         /**
5916          * @event rowcontextmenu
5917          * Fires when a row is right clicked
5918          * @param {Roo.bootstrap.Table} this
5919          * @param {Number} rowIndex
5920          * @param {Roo.EventObject} e
5921          */
5922         "rowcontextmenu" : true,
5923         /**
5924          * @event cellcontextmenu
5925          * Fires when a cell is right clicked
5926          * @param {Roo.bootstrap.Table} this
5927          * @param {Number} rowIndex
5928          * @param {Number} cellIndex
5929          * @param {Roo.EventObject} e
5930          */
5931          "cellcontextmenu" : true,
5932          /**
5933          * @event headercontextmenu
5934          * Fires when a header is right clicked
5935          * @param {Roo.bootstrap.Table} this
5936          * @param {Number} columnIndex
5937          * @param {Roo.EventObject} e
5938          */
5939         "headercontextmenu" : true
5940     });
5941 };
5942
5943 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5944     
5945     cls: false,
5946     align: false,
5947     bgcolor: false,
5948     border: false,
5949     cellpadding: false,
5950     cellspacing: false,
5951     frame: false,
5952     rules: false,
5953     sortable: false,
5954     summary: false,
5955     width: false,
5956     striped : false,
5957     scrollBody : false,
5958     bordered: false,
5959     hover:  false,
5960     condensed : false,
5961     responsive : false,
5962     sm : false,
5963     cm : false,
5964     store : false,
5965     loadMask : false,
5966     footerShow : true,
5967     headerShow : true,
5968   
5969     rowSelection : false,
5970     cellSelection : false,
5971     layout : false,
5972     
5973     // Roo.Element - the tbody
5974     mainBody: false,
5975     // Roo.Element - thead element
5976     mainHead: false,
5977     
5978     container: false, // used by gridpanel...
5979     
5980     lazyLoad : false,
5981     
5982     getAutoCreate : function()
5983     {
5984         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5985         
5986         cfg = {
5987             tag: 'table',
5988             cls : 'table',
5989             cn : []
5990         };
5991         if (this.scrollBody) {
5992             cfg.cls += ' table-body-fixed';
5993         }    
5994         if (this.striped) {
5995             cfg.cls += ' table-striped';
5996         }
5997         
5998         if (this.hover) {
5999             cfg.cls += ' table-hover';
6000         }
6001         if (this.bordered) {
6002             cfg.cls += ' table-bordered';
6003         }
6004         if (this.condensed) {
6005             cfg.cls += ' table-condensed';
6006         }
6007         if (this.responsive) {
6008             cfg.cls += ' table-responsive';
6009         }
6010         
6011         if (this.cls) {
6012             cfg.cls+=  ' ' +this.cls;
6013         }
6014         
6015         // this lot should be simplifed...
6016         
6017         if (this.align) {
6018             cfg.align=this.align;
6019         }
6020         if (this.bgcolor) {
6021             cfg.bgcolor=this.bgcolor;
6022         }
6023         if (this.border) {
6024             cfg.border=this.border;
6025         }
6026         if (this.cellpadding) {
6027             cfg.cellpadding=this.cellpadding;
6028         }
6029         if (this.cellspacing) {
6030             cfg.cellspacing=this.cellspacing;
6031         }
6032         if (this.frame) {
6033             cfg.frame=this.frame;
6034         }
6035         if (this.rules) {
6036             cfg.rules=this.rules;
6037         }
6038         if (this.sortable) {
6039             cfg.sortable=this.sortable;
6040         }
6041         if (this.summary) {
6042             cfg.summary=this.summary;
6043         }
6044         if (this.width) {
6045             cfg.width=this.width;
6046         }
6047         if (this.layout) {
6048             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6049         }
6050         
6051         if(this.store || this.cm){
6052             if(this.headerShow){
6053                 cfg.cn.push(this.renderHeader());
6054             }
6055             
6056             cfg.cn.push(this.renderBody());
6057             
6058             if(this.footerShow){
6059                 cfg.cn.push(this.renderFooter());
6060             }
6061             // where does this come from?
6062             //cfg.cls+=  ' TableGrid';
6063         }
6064         
6065         return { cn : [ cfg ] };
6066     },
6067     
6068     initEvents : function()
6069     {   
6070         if(!this.store || !this.cm){
6071             return;
6072         }
6073         if (this.selModel) {
6074             this.selModel.initEvents();
6075         }
6076         
6077         
6078         //Roo.log('initEvents with ds!!!!');
6079         
6080         this.mainBody = this.el.select('tbody', true).first();
6081         this.mainHead = this.el.select('thead', true).first();
6082         
6083         
6084         
6085         
6086         var _this = this;
6087         
6088         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6089             e.on('click', _this.sort, _this);
6090         });
6091         
6092         this.mainBody.on("click", this.onClick, this);
6093         this.mainBody.on("dblclick", this.onDblClick, this);
6094         
6095         // why is this done????? = it breaks dialogs??
6096         //this.parent().el.setStyle('position', 'relative');
6097         
6098         
6099         if (this.footer) {
6100             this.footer.parentId = this.id;
6101             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6102             
6103             if(this.lazyLoad){
6104                 this.el.select('tfoot tr td').first().addClass('hide');
6105             }
6106         } 
6107         
6108         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6109         
6110         this.store.on('load', this.onLoad, this);
6111         this.store.on('beforeload', this.onBeforeLoad, this);
6112         this.store.on('update', this.onUpdate, this);
6113         this.store.on('add', this.onAdd, this);
6114         this.store.on("clear", this.clear, this);
6115         
6116         this.el.on("contextmenu", this.onContextMenu, this);
6117         
6118         this.mainBody.on('scroll', this.onBodyScroll, this);
6119         
6120         
6121     },
6122     
6123     onContextMenu : function(e, t)
6124     {
6125         this.processEvent("contextmenu", e);
6126     },
6127     
6128     processEvent : function(name, e)
6129     {
6130         if (name != 'touchstart' ) {
6131             this.fireEvent(name, e);    
6132         }
6133         
6134         var t = e.getTarget();
6135         
6136         var cell = Roo.get(t);
6137         
6138         if(!cell){
6139             return;
6140         }
6141         
6142         if(cell.findParent('tfoot', false, true)){
6143             return;
6144         }
6145         
6146         if(cell.findParent('thead', false, true)){
6147             
6148             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6149                 cell = Roo.get(t).findParent('th', false, true);
6150                 if (!cell) {
6151                     Roo.log("failed to find th in thead?");
6152                     Roo.log(e.getTarget());
6153                     return;
6154                 }
6155             }
6156             
6157             var cellIndex = cell.dom.cellIndex;
6158             
6159             var ename = name == 'touchstart' ? 'click' : name;
6160             this.fireEvent("header" + ename, this, cellIndex, e);
6161             
6162             return;
6163         }
6164         
6165         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6166             cell = Roo.get(t).findParent('td', false, true);
6167             if (!cell) {
6168                 Roo.log("failed to find th in tbody?");
6169                 Roo.log(e.getTarget());
6170                 return;
6171             }
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         var cellIndex = cell.dom.cellIndex;
6176         var rowIndex = row.dom.rowIndex - 1;
6177         
6178         if(row !== false){
6179             
6180             this.fireEvent("row" + name, this, rowIndex, e);
6181             
6182             if(cell !== false){
6183             
6184                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6185             }
6186         }
6187         
6188     },
6189     
6190     onMouseover : function(e, el)
6191     {
6192         var cell = Roo.get(el);
6193         
6194         if(!cell){
6195             return;
6196         }
6197         
6198         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6199             cell = cell.findParent('td', false, true);
6200         }
6201         
6202         var row = cell.findParent('tr', false, true);
6203         var cellIndex = cell.dom.cellIndex;
6204         var rowIndex = row.dom.rowIndex - 1; // start from 0
6205         
6206         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6207         
6208     },
6209     
6210     onMouseout : function(e, el)
6211     {
6212         var cell = Roo.get(el);
6213         
6214         if(!cell){
6215             return;
6216         }
6217         
6218         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6219             cell = cell.findParent('td', false, true);
6220         }
6221         
6222         var row = cell.findParent('tr', false, true);
6223         var cellIndex = cell.dom.cellIndex;
6224         var rowIndex = row.dom.rowIndex - 1; // start from 0
6225         
6226         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6227         
6228     },
6229     
6230     onClick : function(e, el)
6231     {
6232         var cell = Roo.get(el);
6233         
6234         if(!cell || (!this.cellSelection && !this.rowSelection)){
6235             return;
6236         }
6237         
6238         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6239             cell = cell.findParent('td', false, true);
6240         }
6241         
6242         if(!cell || typeof(cell) == 'undefined'){
6243             return;
6244         }
6245         
6246         var row = cell.findParent('tr', false, true);
6247         
6248         if(!row || typeof(row) == 'undefined'){
6249             return;
6250         }
6251         
6252         var cellIndex = cell.dom.cellIndex;
6253         var rowIndex = this.getRowIndex(row);
6254         
6255         // why??? - should these not be based on SelectionModel?
6256         if(this.cellSelection){
6257             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6258         }
6259         
6260         if(this.rowSelection){
6261             this.fireEvent('rowclick', this, row, rowIndex, e);
6262         }
6263         
6264         
6265     },
6266         
6267     onDblClick : function(e,el)
6268     {
6269         var cell = Roo.get(el);
6270         
6271         if(!cell || (!this.cellSelection && !this.rowSelection)){
6272             return;
6273         }
6274         
6275         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6276             cell = cell.findParent('td', false, true);
6277         }
6278         
6279         if(!cell || typeof(cell) == 'undefined'){
6280             return;
6281         }
6282         
6283         var row = cell.findParent('tr', false, true);
6284         
6285         if(!row || typeof(row) == 'undefined'){
6286             return;
6287         }
6288         
6289         var cellIndex = cell.dom.cellIndex;
6290         var rowIndex = this.getRowIndex(row);
6291         
6292         if(this.cellSelection){
6293             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6294         }
6295         
6296         if(this.rowSelection){
6297             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6298         }
6299     },
6300     
6301     sort : function(e,el)
6302     {
6303         var col = Roo.get(el);
6304         
6305         if(!col.hasClass('sortable')){
6306             return;
6307         }
6308         
6309         var sort = col.attr('sort');
6310         var dir = 'ASC';
6311         
6312         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6313             dir = 'DESC';
6314         }
6315         
6316         this.store.sortInfo = {field : sort, direction : dir};
6317         
6318         if (this.footer) {
6319             Roo.log("calling footer first");
6320             this.footer.onClick('first');
6321         } else {
6322         
6323             this.store.load({ params : { start : 0 } });
6324         }
6325     },
6326     
6327     renderHeader : function()
6328     {
6329         var header = {
6330             tag: 'thead',
6331             cn : []
6332         };
6333         
6334         var cm = this.cm;
6335         this.totalWidth = 0;
6336         
6337         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6338             
6339             var config = cm.config[i];
6340             
6341             var c = {
6342                 tag: 'th',
6343                 style : '',
6344                 html: cm.getColumnHeader(i)
6345             };
6346             
6347             var hh = '';
6348             
6349             if(typeof(config.sortable) != 'undefined' && config.sortable){
6350                 c.cls = 'sortable';
6351                 c.html = '<i class="glyphicon"></i>' + c.html;
6352             }
6353             
6354             if(typeof(config.lgHeader) != 'undefined'){
6355                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6356             }
6357             
6358             if(typeof(config.mdHeader) != 'undefined'){
6359                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6360             }
6361             
6362             if(typeof(config.smHeader) != 'undefined'){
6363                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6364             }
6365             
6366             if(typeof(config.xsHeader) != 'undefined'){
6367                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6368             }
6369             
6370             if(hh.length){
6371                 c.html = hh;
6372             }
6373             
6374             if(typeof(config.tooltip) != 'undefined'){
6375                 c.tooltip = config.tooltip;
6376             }
6377             
6378             if(typeof(config.colspan) != 'undefined'){
6379                 c.colspan = config.colspan;
6380             }
6381             
6382             if(typeof(config.hidden) != 'undefined' && config.hidden){
6383                 c.style += ' display:none;';
6384             }
6385             
6386             if(typeof(config.dataIndex) != 'undefined'){
6387                 c.sort = config.dataIndex;
6388             }
6389             
6390            
6391             
6392             if(typeof(config.align) != 'undefined' && config.align.length){
6393                 c.style += ' text-align:' + config.align + ';';
6394             }
6395             
6396             if(typeof(config.width) != 'undefined'){
6397                 c.style += ' width:' + config.width + 'px;';
6398                 this.totalWidth += config.width;
6399             } else {
6400                 this.totalWidth += 100; // assume minimum of 100 per column?
6401             }
6402             
6403             if(typeof(config.cls) != 'undefined'){
6404                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6405             }
6406             
6407             ['xs','sm','md','lg'].map(function(size){
6408                 
6409                 if(typeof(config[size]) == 'undefined'){
6410                     return;
6411                 }
6412                 
6413                 if (!config[size]) { // 0 = hidden
6414                     c.cls += ' hidden-' + size;
6415                     return;
6416                 }
6417                 
6418                 c.cls += ' col-' + size + '-' + config[size];
6419
6420             });
6421             
6422             header.cn.push(c)
6423         }
6424         
6425         return header;
6426     },
6427     
6428     renderBody : function()
6429     {
6430         var body = {
6431             tag: 'tbody',
6432             cn : [
6433                 {
6434                     tag: 'tr',
6435                     cn : [
6436                         {
6437                             tag : 'td',
6438                             colspan :  this.cm.getColumnCount()
6439                         }
6440                     ]
6441                 }
6442             ]
6443         };
6444         
6445         return body;
6446     },
6447     
6448     renderFooter : function()
6449     {
6450         var footer = {
6451             tag: 'tfoot',
6452             cn : [
6453                 {
6454                     tag: 'tr',
6455                     cn : [
6456                         {
6457                             tag : 'td',
6458                             colspan :  this.cm.getColumnCount()
6459                         }
6460                     ]
6461                 }
6462             ]
6463         };
6464         
6465         return footer;
6466     },
6467     
6468     
6469     
6470     onLoad : function()
6471     {
6472 //        Roo.log('ds onload');
6473         this.clear();
6474         
6475         var _this = this;
6476         var cm = this.cm;
6477         var ds = this.store;
6478         
6479         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6480             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6481             if (_this.store.sortInfo) {
6482                     
6483                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6484                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6485                 }
6486                 
6487                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6488                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6489                 }
6490             }
6491         });
6492         
6493         var tbody =  this.mainBody;
6494               
6495         if(ds.getCount() > 0){
6496             ds.data.each(function(d,rowIndex){
6497                 var row =  this.renderRow(cm, ds, rowIndex);
6498                 
6499                 tbody.createChild(row);
6500                 
6501                 var _this = this;
6502                 
6503                 if(row.cellObjects.length){
6504                     Roo.each(row.cellObjects, function(r){
6505                         _this.renderCellObject(r);
6506                     })
6507                 }
6508                 
6509             }, this);
6510         }
6511         
6512         Roo.each(this.el.select('tbody td', true).elements, function(e){
6513             e.on('mouseover', _this.onMouseover, _this);
6514         });
6515         
6516         Roo.each(this.el.select('tbody td', true).elements, function(e){
6517             e.on('mouseout', _this.onMouseout, _this);
6518         });
6519         this.fireEvent('rowsrendered', this);
6520         //if(this.loadMask){
6521         //    this.maskEl.hide();
6522         //}
6523         
6524         this.autoSize();
6525     },
6526     
6527     
6528     onUpdate : function(ds,record)
6529     {
6530         this.refreshRow(record);
6531         this.autoSize();
6532     },
6533     
6534     onRemove : function(ds, record, index, isUpdate){
6535         if(isUpdate !== true){
6536             this.fireEvent("beforerowremoved", this, index, record);
6537         }
6538         var bt = this.mainBody.dom;
6539         
6540         var rows = this.el.select('tbody > tr', true).elements;
6541         
6542         if(typeof(rows[index]) != 'undefined'){
6543             bt.removeChild(rows[index].dom);
6544         }
6545         
6546 //        if(bt.rows[index]){
6547 //            bt.removeChild(bt.rows[index]);
6548 //        }
6549         
6550         if(isUpdate !== true){
6551             //this.stripeRows(index);
6552             //this.syncRowHeights(index, index);
6553             //this.layout();
6554             this.fireEvent("rowremoved", this, index, record);
6555         }
6556     },
6557     
6558     onAdd : function(ds, records, rowIndex)
6559     {
6560         //Roo.log('on Add called');
6561         // - note this does not handle multiple adding very well..
6562         var bt = this.mainBody.dom;
6563         for (var i =0 ; i < records.length;i++) {
6564             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6565             //Roo.log(records[i]);
6566             //Roo.log(this.store.getAt(rowIndex+i));
6567             this.insertRow(this.store, rowIndex + i, false);
6568             return;
6569         }
6570         
6571     },
6572     
6573     
6574     refreshRow : function(record){
6575         var ds = this.store, index;
6576         if(typeof record == 'number'){
6577             index = record;
6578             record = ds.getAt(index);
6579         }else{
6580             index = ds.indexOf(record);
6581         }
6582         this.insertRow(ds, index, true);
6583         this.autoSize();
6584         this.onRemove(ds, record, index+1, true);
6585         this.autoSize();
6586         //this.syncRowHeights(index, index);
6587         //this.layout();
6588         this.fireEvent("rowupdated", this, index, record);
6589     },
6590     
6591     insertRow : function(dm, rowIndex, isUpdate){
6592         
6593         if(!isUpdate){
6594             this.fireEvent("beforerowsinserted", this, rowIndex);
6595         }
6596             //var s = this.getScrollState();
6597         var row = this.renderRow(this.cm, this.store, rowIndex);
6598         // insert before rowIndex..
6599         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6600         
6601         var _this = this;
6602                 
6603         if(row.cellObjects.length){
6604             Roo.each(row.cellObjects, function(r){
6605                 _this.renderCellObject(r);
6606             })
6607         }
6608             
6609         if(!isUpdate){
6610             this.fireEvent("rowsinserted", this, rowIndex);
6611             //this.syncRowHeights(firstRow, lastRow);
6612             //this.stripeRows(firstRow);
6613             //this.layout();
6614         }
6615         
6616     },
6617     
6618     
6619     getRowDom : function(rowIndex)
6620     {
6621         var rows = this.el.select('tbody > tr', true).elements;
6622         
6623         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6624         
6625     },
6626     // returns the object tree for a tr..
6627   
6628     
6629     renderRow : function(cm, ds, rowIndex) 
6630     {
6631         
6632         var d = ds.getAt(rowIndex);
6633         
6634         var row = {
6635             tag : 'tr',
6636             cn : []
6637         };
6638             
6639         var cellObjects = [];
6640         
6641         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6642             var config = cm.config[i];
6643             
6644             var renderer = cm.getRenderer(i);
6645             var value = '';
6646             var id = false;
6647             
6648             if(typeof(renderer) !== 'undefined'){
6649                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6650             }
6651             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6652             // and are rendered into the cells after the row is rendered - using the id for the element.
6653             
6654             if(typeof(value) === 'object'){
6655                 id = Roo.id();
6656                 cellObjects.push({
6657                     container : id,
6658                     cfg : value 
6659                 })
6660             }
6661             
6662             var rowcfg = {
6663                 record: d,
6664                 rowIndex : rowIndex,
6665                 colIndex : i,
6666                 rowClass : ''
6667             };
6668
6669             this.fireEvent('rowclass', this, rowcfg);
6670             
6671             var td = {
6672                 tag: 'td',
6673                 cls : rowcfg.rowClass,
6674                 style: '',
6675                 html: (typeof(value) === 'object') ? '' : value
6676             };
6677             
6678             if (id) {
6679                 td.id = id;
6680             }
6681             
6682             if(typeof(config.colspan) != 'undefined'){
6683                 td.colspan = config.colspan;
6684             }
6685             
6686             if(typeof(config.hidden) != 'undefined' && config.hidden){
6687                 td.style += ' display:none;';
6688             }
6689             
6690             if(typeof(config.align) != 'undefined' && config.align.length){
6691                 td.style += ' text-align:' + config.align + ';';
6692             }
6693             
6694             if(typeof(config.width) != 'undefined'){
6695                 td.style += ' width:' +  config.width + 'px;';
6696             }
6697             
6698             if(typeof(config.cursor) != 'undefined'){
6699                 td.style += ' cursor:' +  config.cursor + ';';
6700             }
6701             
6702             if(typeof(config.cls) != 'undefined'){
6703                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6704             }
6705             
6706             ['xs','sm','md','lg'].map(function(size){
6707                 
6708                 if(typeof(config[size]) == 'undefined'){
6709                     return;
6710                 }
6711                 
6712                 if (!config[size]) { // 0 = hidden
6713                     td.cls += ' hidden-' + size;
6714                     return;
6715                 }
6716                 
6717                 td.cls += ' col-' + size + '-' + config[size];
6718
6719             });
6720              
6721             row.cn.push(td);
6722            
6723         }
6724         
6725         row.cellObjects = cellObjects;
6726         
6727         return row;
6728           
6729     },
6730     
6731     
6732     
6733     onBeforeLoad : function()
6734     {
6735         //Roo.log('ds onBeforeLoad');
6736         
6737         //this.clear();
6738         
6739         //if(this.loadMask){
6740         //    this.maskEl.show();
6741         //}
6742     },
6743      /**
6744      * Remove all rows
6745      */
6746     clear : function()
6747     {
6748         this.el.select('tbody', true).first().dom.innerHTML = '';
6749     },
6750     /**
6751      * Show or hide a row.
6752      * @param {Number} rowIndex to show or hide
6753      * @param {Boolean} state hide
6754      */
6755     setRowVisibility : function(rowIndex, state)
6756     {
6757         var bt = this.mainBody.dom;
6758         
6759         var rows = this.el.select('tbody > tr', true).elements;
6760         
6761         if(typeof(rows[rowIndex]) == 'undefined'){
6762             return;
6763         }
6764         rows[rowIndex].dom.style.display = state ? '' : 'none';
6765     },
6766     
6767     
6768     getSelectionModel : function(){
6769         if(!this.selModel){
6770             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6771         }
6772         return this.selModel;
6773     },
6774     /*
6775      * Render the Roo.bootstrap object from renderder
6776      */
6777     renderCellObject : function(r)
6778     {
6779         var _this = this;
6780         
6781         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6782         
6783         var t = r.cfg.render(r.container);
6784         
6785         if(r.cfg.cn){
6786             Roo.each(r.cfg.cn, function(c){
6787                 var child = {
6788                     container: t.getChildContainer(),
6789                     cfg: c
6790                 };
6791                 _this.renderCellObject(child);
6792             })
6793         }
6794     },
6795     
6796     getRowIndex : function(row)
6797     {
6798         var rowIndex = -1;
6799         
6800         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6801             if(el != row){
6802                 return;
6803             }
6804             
6805             rowIndex = index;
6806         });
6807         
6808         return rowIndex;
6809     },
6810      /**
6811      * Returns the grid's underlying element = used by panel.Grid
6812      * @return {Element} The element
6813      */
6814     getGridEl : function(){
6815         return this.el;
6816     },
6817      /**
6818      * Forces a resize - used by panel.Grid
6819      * @return {Element} The element
6820      */
6821     autoSize : function()
6822     {
6823         //var ctr = Roo.get(this.container.dom.parentElement);
6824         var ctr = Roo.get(this.el.dom);
6825         
6826         var thd = this.getGridEl().select('thead',true).first();
6827         var tbd = this.getGridEl().select('tbody', true).first();
6828         var tfd = this.getGridEl().select('tfoot', true).first();
6829         
6830         var cw = ctr.getWidth();
6831         
6832         if (tbd) {
6833             
6834             tbd.setSize(ctr.getWidth(),
6835                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6836             );
6837             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6838             cw -= barsize;
6839         }
6840         cw = Math.max(cw, this.totalWidth);
6841         this.getGridEl().select('tr',true).setWidth(cw);
6842         // resize 'expandable coloumn?
6843         
6844         return; // we doe not have a view in this design..
6845         
6846     },
6847     onBodyScroll: function()
6848     {
6849         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6850         this.mainHead.setStyle({
6851             'position' : 'relative',
6852             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6853         });
6854         
6855         if(this.lazyLoad){
6856             
6857             var scrollHeight = this.mainBody.dom.scrollHeight;
6858             
6859             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6860             
6861             var height = this.mainBody.getHeight();
6862             
6863             if(scrollHeight - height == scrollTop) {
6864                 
6865                 var total = this.ds.getTotalCount();
6866                 
6867                 if(this.footer.cursor + this.footer.pageSize < total){
6868                     
6869                     this.footer.ds.load({
6870                         params : {
6871                             start : this.footer.cursor + this.footer.pageSize,
6872                             limit : this.footer.pageSize
6873                         },
6874                         add : true
6875                     });
6876                 }
6877             }
6878             
6879         }
6880     }
6881 });
6882
6883  
6884
6885  /*
6886  * - LGPL
6887  *
6888  * table cell
6889  * 
6890  */
6891
6892 /**
6893  * @class Roo.bootstrap.TableCell
6894  * @extends Roo.bootstrap.Component
6895  * Bootstrap TableCell class
6896  * @cfg {String} html cell contain text
6897  * @cfg {String} cls cell class
6898  * @cfg {String} tag cell tag (td|th) default td
6899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6900  * @cfg {String} align Aligns the content in a cell
6901  * @cfg {String} axis Categorizes cells
6902  * @cfg {String} bgcolor Specifies the background color of a cell
6903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6904  * @cfg {Number} colspan Specifies the number of columns a cell should span
6905  * @cfg {String} headers Specifies one or more header cells a cell is related to
6906  * @cfg {Number} height Sets the height of a cell
6907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6908  * @cfg {Number} rowspan Sets the number of rows a cell should span
6909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6910  * @cfg {String} valign Vertical aligns the content in a cell
6911  * @cfg {Number} width Specifies the width of a cell
6912  * 
6913  * @constructor
6914  * Create a new TableCell
6915  * @param {Object} config The config object
6916  */
6917
6918 Roo.bootstrap.TableCell = function(config){
6919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6920 };
6921
6922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6923     
6924     html: false,
6925     cls: false,
6926     tag: false,
6927     abbr: false,
6928     align: false,
6929     axis: false,
6930     bgcolor: false,
6931     charoff: false,
6932     colspan: false,
6933     headers: false,
6934     height: false,
6935     nowrap: false,
6936     rowspan: false,
6937     scope: false,
6938     valign: false,
6939     width: false,
6940     
6941     
6942     getAutoCreate : function(){
6943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6944         
6945         cfg = {
6946             tag: 'td'
6947         };
6948         
6949         if(this.tag){
6950             cfg.tag = this.tag;
6951         }
6952         
6953         if (this.html) {
6954             cfg.html=this.html
6955         }
6956         if (this.cls) {
6957             cfg.cls=this.cls
6958         }
6959         if (this.abbr) {
6960             cfg.abbr=this.abbr
6961         }
6962         if (this.align) {
6963             cfg.align=this.align
6964         }
6965         if (this.axis) {
6966             cfg.axis=this.axis
6967         }
6968         if (this.bgcolor) {
6969             cfg.bgcolor=this.bgcolor
6970         }
6971         if (this.charoff) {
6972             cfg.charoff=this.charoff
6973         }
6974         if (this.colspan) {
6975             cfg.colspan=this.colspan
6976         }
6977         if (this.headers) {
6978             cfg.headers=this.headers
6979         }
6980         if (this.height) {
6981             cfg.height=this.height
6982         }
6983         if (this.nowrap) {
6984             cfg.nowrap=this.nowrap
6985         }
6986         if (this.rowspan) {
6987             cfg.rowspan=this.rowspan
6988         }
6989         if (this.scope) {
6990             cfg.scope=this.scope
6991         }
6992         if (this.valign) {
6993             cfg.valign=this.valign
6994         }
6995         if (this.width) {
6996             cfg.width=this.width
6997         }
6998         
6999         
7000         return cfg;
7001     }
7002    
7003 });
7004
7005  
7006
7007  /*
7008  * - LGPL
7009  *
7010  * table row
7011  * 
7012  */
7013
7014 /**
7015  * @class Roo.bootstrap.TableRow
7016  * @extends Roo.bootstrap.Component
7017  * Bootstrap TableRow class
7018  * @cfg {String} cls row class
7019  * @cfg {String} align Aligns the content in a table row
7020  * @cfg {String} bgcolor Specifies a background color for a table row
7021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7022  * @cfg {String} valign Vertical aligns the content in a table row
7023  * 
7024  * @constructor
7025  * Create a new TableRow
7026  * @param {Object} config The config object
7027  */
7028
7029 Roo.bootstrap.TableRow = function(config){
7030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7031 };
7032
7033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7034     
7035     cls: false,
7036     align: false,
7037     bgcolor: false,
7038     charoff: false,
7039     valign: false,
7040     
7041     getAutoCreate : function(){
7042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7043         
7044         cfg = {
7045             tag: 'tr'
7046         };
7047             
7048         if(this.cls){
7049             cfg.cls = this.cls;
7050         }
7051         if(this.align){
7052             cfg.align = this.align;
7053         }
7054         if(this.bgcolor){
7055             cfg.bgcolor = this.bgcolor;
7056         }
7057         if(this.charoff){
7058             cfg.charoff = this.charoff;
7059         }
7060         if(this.valign){
7061             cfg.valign = this.valign;
7062         }
7063         
7064         return cfg;
7065     }
7066    
7067 });
7068
7069  
7070
7071  /*
7072  * - LGPL
7073  *
7074  * table body
7075  * 
7076  */
7077
7078 /**
7079  * @class Roo.bootstrap.TableBody
7080  * @extends Roo.bootstrap.Component
7081  * Bootstrap TableBody class
7082  * @cfg {String} cls element class
7083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7084  * @cfg {String} align Aligns the content inside the element
7085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7087  * 
7088  * @constructor
7089  * Create a new TableBody
7090  * @param {Object} config The config object
7091  */
7092
7093 Roo.bootstrap.TableBody = function(config){
7094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7095 };
7096
7097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7098     
7099     cls: false,
7100     tag: false,
7101     align: false,
7102     charoff: false,
7103     valign: false,
7104     
7105     getAutoCreate : function(){
7106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7107         
7108         cfg = {
7109             tag: 'tbody'
7110         };
7111             
7112         if (this.cls) {
7113             cfg.cls=this.cls
7114         }
7115         if(this.tag){
7116             cfg.tag = this.tag;
7117         }
7118         
7119         if(this.align){
7120             cfg.align = this.align;
7121         }
7122         if(this.charoff){
7123             cfg.charoff = this.charoff;
7124         }
7125         if(this.valign){
7126             cfg.valign = this.valign;
7127         }
7128         
7129         return cfg;
7130     }
7131     
7132     
7133 //    initEvents : function()
7134 //    {
7135 //        
7136 //        if(!this.store){
7137 //            return;
7138 //        }
7139 //        
7140 //        this.store = Roo.factory(this.store, Roo.data);
7141 //        this.store.on('load', this.onLoad, this);
7142 //        
7143 //        this.store.load();
7144 //        
7145 //    },
7146 //    
7147 //    onLoad: function () 
7148 //    {   
7149 //        this.fireEvent('load', this);
7150 //    }
7151 //    
7152 //   
7153 });
7154
7155  
7156
7157  /*
7158  * Based on:
7159  * Ext JS Library 1.1.1
7160  * Copyright(c) 2006-2007, Ext JS, LLC.
7161  *
7162  * Originally Released Under LGPL - original licence link has changed is not relivant.
7163  *
7164  * Fork - LGPL
7165  * <script type="text/javascript">
7166  */
7167
7168 // as we use this in bootstrap.
7169 Roo.namespace('Roo.form');
7170  /**
7171  * @class Roo.form.Action
7172  * Internal Class used to handle form actions
7173  * @constructor
7174  * @param {Roo.form.BasicForm} el The form element or its id
7175  * @param {Object} config Configuration options
7176  */
7177
7178  
7179  
7180 // define the action interface
7181 Roo.form.Action = function(form, options){
7182     this.form = form;
7183     this.options = options || {};
7184 };
7185 /**
7186  * Client Validation Failed
7187  * @const 
7188  */
7189 Roo.form.Action.CLIENT_INVALID = 'client';
7190 /**
7191  * Server Validation Failed
7192  * @const 
7193  */
7194 Roo.form.Action.SERVER_INVALID = 'server';
7195  /**
7196  * Connect to Server Failed
7197  * @const 
7198  */
7199 Roo.form.Action.CONNECT_FAILURE = 'connect';
7200 /**
7201  * Reading Data from Server Failed
7202  * @const 
7203  */
7204 Roo.form.Action.LOAD_FAILURE = 'load';
7205
7206 Roo.form.Action.prototype = {
7207     type : 'default',
7208     failureType : undefined,
7209     response : undefined,
7210     result : undefined,
7211
7212     // interface method
7213     run : function(options){
7214
7215     },
7216
7217     // interface method
7218     success : function(response){
7219
7220     },
7221
7222     // interface method
7223     handleResponse : function(response){
7224
7225     },
7226
7227     // default connection failure
7228     failure : function(response){
7229         
7230         this.response = response;
7231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7232         this.form.afterAction(this, false);
7233     },
7234
7235     processResponse : function(response){
7236         this.response = response;
7237         if(!response.responseText){
7238             return true;
7239         }
7240         this.result = this.handleResponse(response);
7241         return this.result;
7242     },
7243
7244     // utility functions used internally
7245     getUrl : function(appendParams){
7246         var url = this.options.url || this.form.url || this.form.el.dom.action;
7247         if(appendParams){
7248             var p = this.getParams();
7249             if(p){
7250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7251             }
7252         }
7253         return url;
7254     },
7255
7256     getMethod : function(){
7257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7258     },
7259
7260     getParams : function(){
7261         var bp = this.form.baseParams;
7262         var p = this.options.params;
7263         if(p){
7264             if(typeof p == "object"){
7265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7266             }else if(typeof p == 'string' && bp){
7267                 p += '&' + Roo.urlEncode(bp);
7268             }
7269         }else if(bp){
7270             p = Roo.urlEncode(bp);
7271         }
7272         return p;
7273     },
7274
7275     createCallback : function(){
7276         return {
7277             success: this.success,
7278             failure: this.failure,
7279             scope: this,
7280             timeout: (this.form.timeout*1000),
7281             upload: this.form.fileUpload ? this.success : undefined
7282         };
7283     }
7284 };
7285
7286 Roo.form.Action.Submit = function(form, options){
7287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7288 };
7289
7290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7291     type : 'submit',
7292
7293     haveProgress : false,
7294     uploadComplete : false,
7295     
7296     // uploadProgress indicator.
7297     uploadProgress : function()
7298     {
7299         if (!this.form.progressUrl) {
7300             return;
7301         }
7302         
7303         if (!this.haveProgress) {
7304             Roo.MessageBox.progress("Uploading", "Uploading");
7305         }
7306         if (this.uploadComplete) {
7307            Roo.MessageBox.hide();
7308            return;
7309         }
7310         
7311         this.haveProgress = true;
7312    
7313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7314         
7315         var c = new Roo.data.Connection();
7316         c.request({
7317             url : this.form.progressUrl,
7318             params: {
7319                 id : uid
7320             },
7321             method: 'GET',
7322             success : function(req){
7323                //console.log(data);
7324                 var rdata = false;
7325                 var edata;
7326                 try  {
7327                    rdata = Roo.decode(req.responseText)
7328                 } catch (e) {
7329                     Roo.log("Invalid data from server..");
7330                     Roo.log(edata);
7331                     return;
7332                 }
7333                 if (!rdata || !rdata.success) {
7334                     Roo.log(rdata);
7335                     Roo.MessageBox.alert(Roo.encode(rdata));
7336                     return;
7337                 }
7338                 var data = rdata.data;
7339                 
7340                 if (this.uploadComplete) {
7341                    Roo.MessageBox.hide();
7342                    return;
7343                 }
7344                    
7345                 if (data){
7346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7348                     );
7349                 }
7350                 this.uploadProgress.defer(2000,this);
7351             },
7352        
7353             failure: function(data) {
7354                 Roo.log('progress url failed ');
7355                 Roo.log(data);
7356             },
7357             scope : this
7358         });
7359            
7360     },
7361     
7362     
7363     run : function()
7364     {
7365         // run get Values on the form, so it syncs any secondary forms.
7366         this.form.getValues();
7367         
7368         var o = this.options;
7369         var method = this.getMethod();
7370         var isPost = method == 'POST';
7371         if(o.clientValidation === false || this.form.isValid()){
7372             
7373             if (this.form.progressUrl) {
7374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7375                     (new Date() * 1) + '' + Math.random());
7376                     
7377             } 
7378             
7379             
7380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7381                 form:this.form.el.dom,
7382                 url:this.getUrl(!isPost),
7383                 method: method,
7384                 params:isPost ? this.getParams() : null,
7385                 isUpload: this.form.fileUpload
7386             }));
7387             
7388             this.uploadProgress();
7389
7390         }else if (o.clientValidation !== false){ // client validation failed
7391             this.failureType = Roo.form.Action.CLIENT_INVALID;
7392             this.form.afterAction(this, false);
7393         }
7394     },
7395
7396     success : function(response)
7397     {
7398         this.uploadComplete= true;
7399         if (this.haveProgress) {
7400             Roo.MessageBox.hide();
7401         }
7402         
7403         
7404         var result = this.processResponse(response);
7405         if(result === true || result.success){
7406             this.form.afterAction(this, true);
7407             return;
7408         }
7409         if(result.errors){
7410             this.form.markInvalid(result.errors);
7411             this.failureType = Roo.form.Action.SERVER_INVALID;
7412         }
7413         this.form.afterAction(this, false);
7414     },
7415     failure : function(response)
7416     {
7417         this.uploadComplete= true;
7418         if (this.haveProgress) {
7419             Roo.MessageBox.hide();
7420         }
7421         
7422         this.response = response;
7423         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7424         this.form.afterAction(this, false);
7425     },
7426     
7427     handleResponse : function(response){
7428         if(this.form.errorReader){
7429             var rs = this.form.errorReader.read(response);
7430             var errors = [];
7431             if(rs.records){
7432                 for(var i = 0, len = rs.records.length; i < len; i++) {
7433                     var r = rs.records[i];
7434                     errors[i] = r.data;
7435                 }
7436             }
7437             if(errors.length < 1){
7438                 errors = null;
7439             }
7440             return {
7441                 success : rs.success,
7442                 errors : errors
7443             };
7444         }
7445         var ret = false;
7446         try {
7447             ret = Roo.decode(response.responseText);
7448         } catch (e) {
7449             ret = {
7450                 success: false,
7451                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7452                 errors : []
7453             };
7454         }
7455         return ret;
7456         
7457     }
7458 });
7459
7460
7461 Roo.form.Action.Load = function(form, options){
7462     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7463     this.reader = this.form.reader;
7464 };
7465
7466 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7467     type : 'load',
7468
7469     run : function(){
7470         
7471         Roo.Ajax.request(Roo.apply(
7472                 this.createCallback(), {
7473                     method:this.getMethod(),
7474                     url:this.getUrl(false),
7475                     params:this.getParams()
7476         }));
7477     },
7478
7479     success : function(response){
7480         
7481         var result = this.processResponse(response);
7482         if(result === true || !result.success || !result.data){
7483             this.failureType = Roo.form.Action.LOAD_FAILURE;
7484             this.form.afterAction(this, false);
7485             return;
7486         }
7487         this.form.clearInvalid();
7488         this.form.setValues(result.data);
7489         this.form.afterAction(this, true);
7490     },
7491
7492     handleResponse : function(response){
7493         if(this.form.reader){
7494             var rs = this.form.reader.read(response);
7495             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7496             return {
7497                 success : rs.success,
7498                 data : data
7499             };
7500         }
7501         return Roo.decode(response.responseText);
7502     }
7503 });
7504
7505 Roo.form.Action.ACTION_TYPES = {
7506     'load' : Roo.form.Action.Load,
7507     'submit' : Roo.form.Action.Submit
7508 };/*
7509  * - LGPL
7510  *
7511  * form
7512  *
7513  */
7514
7515 /**
7516  * @class Roo.bootstrap.Form
7517  * @extends Roo.bootstrap.Component
7518  * Bootstrap Form class
7519  * @cfg {String} method  GET | POST (default POST)
7520  * @cfg {String} labelAlign top | left (default top)
7521  * @cfg {String} align left  | right - for navbars
7522  * @cfg {Boolean} loadMask load mask when submit (default true)
7523
7524  *
7525  * @constructor
7526  * Create a new Form
7527  * @param {Object} config The config object
7528  */
7529
7530
7531 Roo.bootstrap.Form = function(config){
7532     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7533     
7534     Roo.bootstrap.Form.popover.apply();
7535     
7536     this.addEvents({
7537         /**
7538          * @event clientvalidation
7539          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7540          * @param {Form} this
7541          * @param {Boolean} valid true if the form has passed client-side validation
7542          */
7543         clientvalidation: true,
7544         /**
7545          * @event beforeaction
7546          * Fires before any action is performed. Return false to cancel the action.
7547          * @param {Form} this
7548          * @param {Action} action The action to be performed
7549          */
7550         beforeaction: true,
7551         /**
7552          * @event actionfailed
7553          * Fires when an action fails.
7554          * @param {Form} this
7555          * @param {Action} action The action that failed
7556          */
7557         actionfailed : true,
7558         /**
7559          * @event actioncomplete
7560          * Fires when an action is completed.
7561          * @param {Form} this
7562          * @param {Action} action The action that completed
7563          */
7564         actioncomplete : true
7565     });
7566
7567 };
7568
7569 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7570
7571      /**
7572      * @cfg {String} method
7573      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7574      */
7575     method : 'POST',
7576     /**
7577      * @cfg {String} url
7578      * The URL to use for form actions if one isn't supplied in the action options.
7579      */
7580     /**
7581      * @cfg {Boolean} fileUpload
7582      * Set to true if this form is a file upload.
7583      */
7584
7585     /**
7586      * @cfg {Object} baseParams
7587      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7588      */
7589
7590     /**
7591      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7592      */
7593     timeout: 30,
7594     /**
7595      * @cfg {Sting} align (left|right) for navbar forms
7596      */
7597     align : 'left',
7598
7599     // private
7600     activeAction : null,
7601
7602     /**
7603      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7604      * element by passing it or its id or mask the form itself by passing in true.
7605      * @type Mixed
7606      */
7607     waitMsgTarget : false,
7608
7609     loadMask : true,
7610     
7611     /**
7612      * @cfg {Boolean} errorMask (true|false) default false
7613      */
7614     errorMask : false,
7615     
7616     /**
7617      * @cfg {Number} maskOffset Default 100
7618      */
7619     maskOffset : 100,
7620
7621     getAutoCreate : function(){
7622
7623         var cfg = {
7624             tag: 'form',
7625             method : this.method || 'POST',
7626             id : this.id || Roo.id(),
7627             cls : ''
7628         };
7629         if (this.parent().xtype.match(/^Nav/)) {
7630             cfg.cls = 'navbar-form navbar-' + this.align;
7631
7632         }
7633
7634         if (this.labelAlign == 'left' ) {
7635             cfg.cls += ' form-horizontal';
7636         }
7637
7638
7639         return cfg;
7640     },
7641     initEvents : function()
7642     {
7643         this.el.on('submit', this.onSubmit, this);
7644         // this was added as random key presses on the form where triggering form submit.
7645         this.el.on('keypress', function(e) {
7646             if (e.getCharCode() != 13) {
7647                 return true;
7648             }
7649             // we might need to allow it for textareas.. and some other items.
7650             // check e.getTarget().
7651
7652             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7653                 return true;
7654             }
7655
7656             Roo.log("keypress blocked");
7657
7658             e.preventDefault();
7659             return false;
7660         });
7661         
7662     },
7663     // private
7664     onSubmit : function(e){
7665         e.stopEvent();
7666     },
7667
7668      /**
7669      * Returns true if client-side validation on the form is successful.
7670      * @return Boolean
7671      */
7672     isValid : function(){
7673         var items = this.getItems();
7674         var valid = true;
7675         var target = false;
7676         
7677         items.each(function(f){
7678             if(f.validate()){
7679                 return;
7680             }
7681             valid = false;
7682
7683             if(!target && f.el.isVisible(true)){
7684                 target = f;
7685             }
7686            
7687         });
7688         
7689         if(this.errorMask && !valid){
7690             Roo.bootstrap.Form.popover.mask(this, target);
7691         }
7692         
7693         return valid;
7694     },
7695     
7696     /**
7697      * Returns true if any fields in this form have changed since their original load.
7698      * @return Boolean
7699      */
7700     isDirty : function(){
7701         var dirty = false;
7702         var items = this.getItems();
7703         items.each(function(f){
7704            if(f.isDirty()){
7705                dirty = true;
7706                return false;
7707            }
7708            return true;
7709         });
7710         return dirty;
7711     },
7712      /**
7713      * Performs a predefined action (submit or load) or custom actions you define on this form.
7714      * @param {String} actionName The name of the action type
7715      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7716      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7717      * accept other config options):
7718      * <pre>
7719 Property          Type             Description
7720 ----------------  ---------------  ----------------------------------------------------------------------------------
7721 url               String           The url for the action (defaults to the form's url)
7722 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7723 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7724 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7725                                    validate the form on the client (defaults to false)
7726      * </pre>
7727      * @return {BasicForm} this
7728      */
7729     doAction : function(action, options){
7730         if(typeof action == 'string'){
7731             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7732         }
7733         if(this.fireEvent('beforeaction', this, action) !== false){
7734             this.beforeAction(action);
7735             action.run.defer(100, action);
7736         }
7737         return this;
7738     },
7739
7740     // private
7741     beforeAction : function(action){
7742         var o = action.options;
7743
7744         if(this.loadMask){
7745             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7746         }
7747         // not really supported yet.. ??
7748
7749         //if(this.waitMsgTarget === true){
7750         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7751         //}else if(this.waitMsgTarget){
7752         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7753         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7754         //}else {
7755         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7756        // }
7757
7758     },
7759
7760     // private
7761     afterAction : function(action, success){
7762         this.activeAction = null;
7763         var o = action.options;
7764
7765         //if(this.waitMsgTarget === true){
7766             this.el.unmask();
7767         //}else if(this.waitMsgTarget){
7768         //    this.waitMsgTarget.unmask();
7769         //}else{
7770         //    Roo.MessageBox.updateProgress(1);
7771         //    Roo.MessageBox.hide();
7772        // }
7773         //
7774         if(success){
7775             if(o.reset){
7776                 this.reset();
7777             }
7778             Roo.callback(o.success, o.scope, [this, action]);
7779             this.fireEvent('actioncomplete', this, action);
7780
7781         }else{
7782
7783             // failure condition..
7784             // we have a scenario where updates need confirming.
7785             // eg. if a locking scenario exists..
7786             // we look for { errors : { needs_confirm : true }} in the response.
7787             if (
7788                 (typeof(action.result) != 'undefined')  &&
7789                 (typeof(action.result.errors) != 'undefined')  &&
7790                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7791            ){
7792                 var _t = this;
7793                 Roo.log("not supported yet");
7794                  /*
7795
7796                 Roo.MessageBox.confirm(
7797                     "Change requires confirmation",
7798                     action.result.errorMsg,
7799                     function(r) {
7800                         if (r != 'yes') {
7801                             return;
7802                         }
7803                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7804                     }
7805
7806                 );
7807                 */
7808
7809
7810                 return;
7811             }
7812
7813             Roo.callback(o.failure, o.scope, [this, action]);
7814             // show an error message if no failed handler is set..
7815             if (!this.hasListener('actionfailed')) {
7816                 Roo.log("need to add dialog support");
7817                 /*
7818                 Roo.MessageBox.alert("Error",
7819                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7820                         action.result.errorMsg :
7821                         "Saving Failed, please check your entries or try again"
7822                 );
7823                 */
7824             }
7825
7826             this.fireEvent('actionfailed', this, action);
7827         }
7828
7829     },
7830     /**
7831      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7832      * @param {String} id The value to search for
7833      * @return Field
7834      */
7835     findField : function(id){
7836         var items = this.getItems();
7837         var field = items.get(id);
7838         if(!field){
7839              items.each(function(f){
7840                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7841                     field = f;
7842                     return false;
7843                 }
7844                 return true;
7845             });
7846         }
7847         return field || null;
7848     },
7849      /**
7850      * Mark fields in this form invalid in bulk.
7851      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7852      * @return {BasicForm} this
7853      */
7854     markInvalid : function(errors){
7855         if(errors instanceof Array){
7856             for(var i = 0, len = errors.length; i < len; i++){
7857                 var fieldError = errors[i];
7858                 var f = this.findField(fieldError.id);
7859                 if(f){
7860                     f.markInvalid(fieldError.msg);
7861                 }
7862             }
7863         }else{
7864             var field, id;
7865             for(id in errors){
7866                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7867                     field.markInvalid(errors[id]);
7868                 }
7869             }
7870         }
7871         //Roo.each(this.childForms || [], function (f) {
7872         //    f.markInvalid(errors);
7873         //});
7874
7875         return this;
7876     },
7877
7878     /**
7879      * Set values for fields in this form in bulk.
7880      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7881      * @return {BasicForm} this
7882      */
7883     setValues : function(values){
7884         if(values instanceof Array){ // array of objects
7885             for(var i = 0, len = values.length; i < len; i++){
7886                 var v = values[i];
7887                 var f = this.findField(v.id);
7888                 if(f){
7889                     f.setValue(v.value);
7890                     if(this.trackResetOnLoad){
7891                         f.originalValue = f.getValue();
7892                     }
7893                 }
7894             }
7895         }else{ // object hash
7896             var field, id;
7897             for(id in values){
7898                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7899
7900                     if (field.setFromData &&
7901                         field.valueField &&
7902                         field.displayField &&
7903                         // combos' with local stores can
7904                         // be queried via setValue()
7905                         // to set their value..
7906                         (field.store && !field.store.isLocal)
7907                         ) {
7908                         // it's a combo
7909                         var sd = { };
7910                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7911                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7912                         field.setFromData(sd);
7913
7914                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7915                         
7916                         field.setFromData(values);
7917                         
7918                     } else {
7919                         field.setValue(values[id]);
7920                     }
7921
7922
7923                     if(this.trackResetOnLoad){
7924                         field.originalValue = field.getValue();
7925                     }
7926                 }
7927             }
7928         }
7929
7930         //Roo.each(this.childForms || [], function (f) {
7931         //    f.setValues(values);
7932         //});
7933
7934         return this;
7935     },
7936
7937     /**
7938      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7939      * they are returned as an array.
7940      * @param {Boolean} asString
7941      * @return {Object}
7942      */
7943     getValues : function(asString){
7944         //if (this.childForms) {
7945             // copy values from the child forms
7946         //    Roo.each(this.childForms, function (f) {
7947         //        this.setValues(f.getValues());
7948         //    }, this);
7949         //}
7950
7951
7952
7953         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7954         if(asString === true){
7955             return fs;
7956         }
7957         return Roo.urlDecode(fs);
7958     },
7959
7960     /**
7961      * Returns the fields in this form as an object with key/value pairs.
7962      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7963      * @return {Object}
7964      */
7965     getFieldValues : function(with_hidden)
7966     {
7967         var items = this.getItems();
7968         var ret = {};
7969         items.each(function(f){
7970             
7971             if (!f.getName()) {
7972                 return;
7973             }
7974             
7975             var v = f.getValue();
7976             
7977             if (f.inputType =='radio') {
7978                 if (typeof(ret[f.getName()]) == 'undefined') {
7979                     ret[f.getName()] = ''; // empty..
7980                 }
7981
7982                 if (!f.el.dom.checked) {
7983                     return;
7984
7985                 }
7986                 v = f.el.dom.value;
7987
7988             }
7989             
7990             if(f.xtype == 'MoneyField'){
7991                 ret[f.currencyName] = f.getCurrency();
7992             }
7993
7994             // not sure if this supported any more..
7995             if ((typeof(v) == 'object') && f.getRawValue) {
7996                 v = f.getRawValue() ; // dates..
7997             }
7998             // combo boxes where name != hiddenName...
7999             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8000                 ret[f.name] = f.getRawValue();
8001             }
8002             ret[f.getName()] = v;
8003         });
8004
8005         return ret;
8006     },
8007
8008     /**
8009      * Clears all invalid messages in this form.
8010      * @return {BasicForm} this
8011      */
8012     clearInvalid : function(){
8013         var items = this.getItems();
8014
8015         items.each(function(f){
8016            f.clearInvalid();
8017         });
8018
8019
8020
8021         return this;
8022     },
8023
8024     /**
8025      * Resets this form.
8026      * @return {BasicForm} this
8027      */
8028     reset : function(){
8029         var items = this.getItems();
8030         items.each(function(f){
8031             f.reset();
8032         });
8033
8034         Roo.each(this.childForms || [], function (f) {
8035             f.reset();
8036         });
8037
8038
8039         return this;
8040     },
8041     getItems : function()
8042     {
8043         var r=new Roo.util.MixedCollection(false, function(o){
8044             return o.id || (o.id = Roo.id());
8045         });
8046         var iter = function(el) {
8047             if (el.inputEl) {
8048                 r.add(el);
8049             }
8050             if (!el.items) {
8051                 return;
8052             }
8053             Roo.each(el.items,function(e) {
8054                 iter(e);
8055             });
8056
8057
8058         };
8059
8060         iter(this);
8061         return r;
8062
8063
8064
8065
8066     }
8067
8068 });
8069
8070 Roo.apply(Roo.bootstrap.Form, {
8071     
8072     popover : {
8073         
8074         padding : 5,
8075         
8076         isApplied : false,
8077         
8078         isMasked : false,
8079         
8080         form : false,
8081         
8082         target : false,
8083         
8084         toolTip : false,
8085         
8086         intervalID : false,
8087         
8088         maskEl : false,
8089         
8090         apply : function()
8091         {
8092             if(this.isApplied){
8093                 return;
8094             }
8095             
8096             this.maskEl = {
8097                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8098                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8099                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8100                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8101             };
8102             
8103             this.maskEl.top.enableDisplayMode("block");
8104             this.maskEl.left.enableDisplayMode("block");
8105             this.maskEl.bottom.enableDisplayMode("block");
8106             this.maskEl.right.enableDisplayMode("block");
8107             
8108             this.toolTip = new Roo.bootstrap.Tooltip({
8109                 cls : 'roo-form-error-popover',
8110                 alignment : {
8111                     'left' : ['r-l', [-2,0], 'right'],
8112                     'right' : ['l-r', [2,0], 'left'],
8113                     'bottom' : ['tl-bl', [0,2], 'top'],
8114                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8115                 }
8116             });
8117             
8118             this.toolTip.render(Roo.get(document.body));
8119
8120             this.toolTip.el.enableDisplayMode("block");
8121             
8122             Roo.get(document.body).on('click', function(){
8123                 this.unmask();
8124             }, this);
8125             
8126             Roo.get(document.body).on('touchstart', function(){
8127                 this.unmask();
8128             }, this);
8129             
8130             this.isApplied = true
8131         },
8132         
8133         mask : function(form, target)
8134         {
8135             this.form = form;
8136             
8137             this.target = target;
8138             
8139             if(!this.form.errorMask || !target.el){
8140                 return;
8141             }
8142             
8143             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8144             
8145             Roo.log(scrollable);
8146             
8147             var ot = this.target.el.calcOffsetsTo(scrollable);
8148             
8149             var scrollTo = ot[1] - this.form.maskOffset;
8150             
8151             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8152             
8153             scrollable.scrollTo('top', scrollTo);
8154             
8155             var box = this.target.el.getBox();
8156             Roo.log(box);
8157             var zIndex = Roo.bootstrap.Modal.zIndex++;
8158
8159             
8160             this.maskEl.top.setStyle('position', 'absolute');
8161             this.maskEl.top.setStyle('z-index', zIndex);
8162             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8163             this.maskEl.top.setLeft(0);
8164             this.maskEl.top.setTop(0);
8165             this.maskEl.top.show();
8166             
8167             this.maskEl.left.setStyle('position', 'absolute');
8168             this.maskEl.left.setStyle('z-index', zIndex);
8169             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8170             this.maskEl.left.setLeft(0);
8171             this.maskEl.left.setTop(box.y - this.padding);
8172             this.maskEl.left.show();
8173
8174             this.maskEl.bottom.setStyle('position', 'absolute');
8175             this.maskEl.bottom.setStyle('z-index', zIndex);
8176             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8177             this.maskEl.bottom.setLeft(0);
8178             this.maskEl.bottom.setTop(box.bottom + this.padding);
8179             this.maskEl.bottom.show();
8180
8181             this.maskEl.right.setStyle('position', 'absolute');
8182             this.maskEl.right.setStyle('z-index', zIndex);
8183             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8184             this.maskEl.right.setLeft(box.right + this.padding);
8185             this.maskEl.right.setTop(box.y - this.padding);
8186             this.maskEl.right.show();
8187
8188             this.toolTip.bindEl = this.target.el;
8189
8190             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8191
8192             var tip = this.target.blankText;
8193
8194             if(this.target.getValue() !== '' ) {
8195                 
8196                 if (this.target.invalidText.length) {
8197                     tip = this.target.invalidText;
8198                 } else if (this.target.regexText.length){
8199                     tip = this.target.regexText;
8200                 }
8201             }
8202
8203             this.toolTip.show(tip);
8204
8205             this.intervalID = window.setInterval(function() {
8206                 Roo.bootstrap.Form.popover.unmask();
8207             }, 10000);
8208
8209             window.onwheel = function(){ return false;};
8210             
8211             (function(){ this.isMasked = true; }).defer(500, this);
8212             
8213         },
8214         
8215         unmask : function()
8216         {
8217             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8218                 return;
8219             }
8220             
8221             this.maskEl.top.setStyle('position', 'absolute');
8222             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8223             this.maskEl.top.hide();
8224
8225             this.maskEl.left.setStyle('position', 'absolute');
8226             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8227             this.maskEl.left.hide();
8228
8229             this.maskEl.bottom.setStyle('position', 'absolute');
8230             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8231             this.maskEl.bottom.hide();
8232
8233             this.maskEl.right.setStyle('position', 'absolute');
8234             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8235             this.maskEl.right.hide();
8236             
8237             this.toolTip.hide();
8238             
8239             this.toolTip.el.hide();
8240             
8241             window.onwheel = function(){ return true;};
8242             
8243             if(this.intervalID){
8244                 window.clearInterval(this.intervalID);
8245                 this.intervalID = false;
8246             }
8247             
8248             this.isMasked = false;
8249             
8250         }
8251         
8252     }
8253     
8254 });
8255
8256 /*
8257  * Based on:
8258  * Ext JS Library 1.1.1
8259  * Copyright(c) 2006-2007, Ext JS, LLC.
8260  *
8261  * Originally Released Under LGPL - original licence link has changed is not relivant.
8262  *
8263  * Fork - LGPL
8264  * <script type="text/javascript">
8265  */
8266 /**
8267  * @class Roo.form.VTypes
8268  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8269  * @singleton
8270  */
8271 Roo.form.VTypes = function(){
8272     // closure these in so they are only created once.
8273     var alpha = /^[a-zA-Z_]+$/;
8274     var alphanum = /^[a-zA-Z0-9_]+$/;
8275     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8276     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8277
8278     // All these messages and functions are configurable
8279     return {
8280         /**
8281          * The function used to validate email addresses
8282          * @param {String} value The email address
8283          */
8284         'email' : function(v){
8285             return email.test(v);
8286         },
8287         /**
8288          * The error text to display when the email validation function returns false
8289          * @type String
8290          */
8291         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8292         /**
8293          * The keystroke filter mask to be applied on email input
8294          * @type RegExp
8295          */
8296         'emailMask' : /[a-z0-9_\.\-@]/i,
8297
8298         /**
8299          * The function used to validate URLs
8300          * @param {String} value The URL
8301          */
8302         'url' : function(v){
8303             return url.test(v);
8304         },
8305         /**
8306          * The error text to display when the url validation function returns false
8307          * @type String
8308          */
8309         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8310         
8311         /**
8312          * The function used to validate alpha values
8313          * @param {String} value The value
8314          */
8315         'alpha' : function(v){
8316             return alpha.test(v);
8317         },
8318         /**
8319          * The error text to display when the alpha validation function returns false
8320          * @type String
8321          */
8322         'alphaText' : 'This field should only contain letters and _',
8323         /**
8324          * The keystroke filter mask to be applied on alpha input
8325          * @type RegExp
8326          */
8327         'alphaMask' : /[a-z_]/i,
8328
8329         /**
8330          * The function used to validate alphanumeric values
8331          * @param {String} value The value
8332          */
8333         'alphanum' : function(v){
8334             return alphanum.test(v);
8335         },
8336         /**
8337          * The error text to display when the alphanumeric validation function returns false
8338          * @type String
8339          */
8340         'alphanumText' : 'This field should only contain letters, numbers and _',
8341         /**
8342          * The keystroke filter mask to be applied on alphanumeric input
8343          * @type RegExp
8344          */
8345         'alphanumMask' : /[a-z0-9_]/i
8346     };
8347 }();/*
8348  * - LGPL
8349  *
8350  * Input
8351  * 
8352  */
8353
8354 /**
8355  * @class Roo.bootstrap.Input
8356  * @extends Roo.bootstrap.Component
8357  * Bootstrap Input class
8358  * @cfg {Boolean} disabled is it disabled
8359  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8360  * @cfg {String} name name of the input
8361  * @cfg {string} fieldLabel - the label associated
8362  * @cfg {string} placeholder - placeholder to put in text.
8363  * @cfg {string}  before - input group add on before
8364  * @cfg {string} after - input group add on after
8365  * @cfg {string} size - (lg|sm) or leave empty..
8366  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8367  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8368  * @cfg {Number} md colspan out of 12 for computer-sized screens
8369  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8370  * @cfg {string} value default value of the input
8371  * @cfg {Number} labelWidth set the width of label 
8372  * @cfg {Number} labellg set the width of label (1-12)
8373  * @cfg {Number} labelmd set the width of label (1-12)
8374  * @cfg {Number} labelsm set the width of label (1-12)
8375  * @cfg {Number} labelxs set the width of label (1-12)
8376  * @cfg {String} labelAlign (top|left)
8377  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8378  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8379  * @cfg {String} indicatorpos (left|right) default left
8380
8381  * @cfg {String} align (left|center|right) Default left
8382  * @cfg {Boolean} forceFeedback (true|false) Default false
8383  * 
8384  * 
8385  * 
8386  * 
8387  * @constructor
8388  * Create a new Input
8389  * @param {Object} config The config object
8390  */
8391
8392 Roo.bootstrap.Input = function(config){
8393     
8394     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8395     
8396     this.addEvents({
8397         /**
8398          * @event focus
8399          * Fires when this field receives input focus.
8400          * @param {Roo.form.Field} this
8401          */
8402         focus : true,
8403         /**
8404          * @event blur
8405          * Fires when this field loses input focus.
8406          * @param {Roo.form.Field} this
8407          */
8408         blur : true,
8409         /**
8410          * @event specialkey
8411          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8412          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8413          * @param {Roo.form.Field} this
8414          * @param {Roo.EventObject} e The event object
8415          */
8416         specialkey : true,
8417         /**
8418          * @event change
8419          * Fires just before the field blurs if the field value has changed.
8420          * @param {Roo.form.Field} this
8421          * @param {Mixed} newValue The new value
8422          * @param {Mixed} oldValue The original value
8423          */
8424         change : true,
8425         /**
8426          * @event invalid
8427          * Fires after the field has been marked as invalid.
8428          * @param {Roo.form.Field} this
8429          * @param {String} msg The validation message
8430          */
8431         invalid : true,
8432         /**
8433          * @event valid
8434          * Fires after the field has been validated with no errors.
8435          * @param {Roo.form.Field} this
8436          */
8437         valid : true,
8438          /**
8439          * @event keyup
8440          * Fires after the key up
8441          * @param {Roo.form.Field} this
8442          * @param {Roo.EventObject}  e The event Object
8443          */
8444         keyup : true
8445     });
8446 };
8447
8448 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8449      /**
8450      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8451       automatic validation (defaults to "keyup").
8452      */
8453     validationEvent : "keyup",
8454      /**
8455      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8456      */
8457     validateOnBlur : true,
8458     /**
8459      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8460      */
8461     validationDelay : 250,
8462      /**
8463      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8464      */
8465     focusClass : "x-form-focus",  // not needed???
8466     
8467        
8468     /**
8469      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8470      */
8471     invalidClass : "has-warning",
8472     
8473     /**
8474      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8475      */
8476     validClass : "has-success",
8477     
8478     /**
8479      * @cfg {Boolean} hasFeedback (true|false) default true
8480      */
8481     hasFeedback : true,
8482     
8483     /**
8484      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8485      */
8486     invalidFeedbackClass : "glyphicon-warning-sign",
8487     
8488     /**
8489      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8490      */
8491     validFeedbackClass : "glyphicon-ok",
8492     
8493     /**
8494      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8495      */
8496     selectOnFocus : false,
8497     
8498      /**
8499      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8500      */
8501     maskRe : null,
8502        /**
8503      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8504      */
8505     vtype : null,
8506     
8507       /**
8508      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8509      */
8510     disableKeyFilter : false,
8511     
8512        /**
8513      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8514      */
8515     disabled : false,
8516      /**
8517      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8518      */
8519     allowBlank : true,
8520     /**
8521      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8522      */
8523     blankText : "Please complete this mandatory field",
8524     
8525      /**
8526      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8527      */
8528     minLength : 0,
8529     /**
8530      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8531      */
8532     maxLength : Number.MAX_VALUE,
8533     /**
8534      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8535      */
8536     minLengthText : "The minimum length for this field is {0}",
8537     /**
8538      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8539      */
8540     maxLengthText : "The maximum length for this field is {0}",
8541   
8542     
8543     /**
8544      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8545      * If available, this function will be called only after the basic validators all return true, and will be passed the
8546      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8547      */
8548     validator : null,
8549     /**
8550      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8551      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8552      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8553      */
8554     regex : null,
8555     /**
8556      * @cfg {String} regexText -- Depricated - use Invalid Text
8557      */
8558     regexText : "",
8559     
8560     /**
8561      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8562      */
8563     invalidText : "",
8564     
8565     
8566     
8567     autocomplete: false,
8568     
8569     
8570     fieldLabel : '',
8571     inputType : 'text',
8572     
8573     name : false,
8574     placeholder: false,
8575     before : false,
8576     after : false,
8577     size : false,
8578     hasFocus : false,
8579     preventMark: false,
8580     isFormField : true,
8581     value : '',
8582     labelWidth : 2,
8583     labelAlign : false,
8584     readOnly : false,
8585     align : false,
8586     formatedValue : false,
8587     forceFeedback : false,
8588     
8589     indicatorpos : 'left',
8590     
8591     labellg : 0,
8592     labelmd : 0,
8593     labelsm : 0,
8594     labelxs : 0,
8595     
8596     parentLabelAlign : function()
8597     {
8598         var parent = this;
8599         while (parent.parent()) {
8600             parent = parent.parent();
8601             if (typeof(parent.labelAlign) !='undefined') {
8602                 return parent.labelAlign;
8603             }
8604         }
8605         return 'left';
8606         
8607     },
8608     
8609     getAutoCreate : function()
8610     {
8611         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8612         
8613         var id = Roo.id();
8614         
8615         var cfg = {};
8616         
8617         if(this.inputType != 'hidden'){
8618             cfg.cls = 'form-group' //input-group
8619         }
8620         
8621         var input =  {
8622             tag: 'input',
8623             id : id,
8624             type : this.inputType,
8625             value : this.value,
8626             cls : 'form-control',
8627             placeholder : this.placeholder || '',
8628             autocomplete : this.autocomplete || 'new-password'
8629         };
8630         
8631         if(this.align){
8632             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8633         }
8634         
8635         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8636             input.maxLength = this.maxLength;
8637         }
8638         
8639         if (this.disabled) {
8640             input.disabled=true;
8641         }
8642         
8643         if (this.readOnly) {
8644             input.readonly=true;
8645         }
8646         
8647         if (this.name) {
8648             input.name = this.name;
8649         }
8650         
8651         if (this.size) {
8652             input.cls += ' input-' + this.size;
8653         }
8654         
8655         var settings=this;
8656         ['xs','sm','md','lg'].map(function(size){
8657             if (settings[size]) {
8658                 cfg.cls += ' col-' + size + '-' + settings[size];
8659             }
8660         });
8661         
8662         var inputblock = input;
8663         
8664         var feedback = {
8665             tag: 'span',
8666             cls: 'glyphicon form-control-feedback'
8667         };
8668             
8669         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8670             
8671             inputblock = {
8672                 cls : 'has-feedback',
8673                 cn :  [
8674                     input,
8675                     feedback
8676                 ] 
8677             };  
8678         }
8679         
8680         if (this.before || this.after) {
8681             
8682             inputblock = {
8683                 cls : 'input-group',
8684                 cn :  [] 
8685             };
8686             
8687             if (this.before && typeof(this.before) == 'string') {
8688                 
8689                 inputblock.cn.push({
8690                     tag :'span',
8691                     cls : 'roo-input-before input-group-addon',
8692                     html : this.before
8693                 });
8694             }
8695             if (this.before && typeof(this.before) == 'object') {
8696                 this.before = Roo.factory(this.before);
8697                 
8698                 inputblock.cn.push({
8699                     tag :'span',
8700                     cls : 'roo-input-before input-group-' +
8701                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8702                 });
8703             }
8704             
8705             inputblock.cn.push(input);
8706             
8707             if (this.after && typeof(this.after) == 'string') {
8708                 inputblock.cn.push({
8709                     tag :'span',
8710                     cls : 'roo-input-after input-group-addon',
8711                     html : this.after
8712                 });
8713             }
8714             if (this.after && typeof(this.after) == 'object') {
8715                 this.after = Roo.factory(this.after);
8716                 
8717                 inputblock.cn.push({
8718                     tag :'span',
8719                     cls : 'roo-input-after input-group-' +
8720                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8721                 });
8722             }
8723             
8724             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8725                 inputblock.cls += ' has-feedback';
8726                 inputblock.cn.push(feedback);
8727             }
8728         };
8729         
8730         if (align ==='left' && this.fieldLabel.length) {
8731             
8732             cfg.cls += ' roo-form-group-label-left';
8733             
8734             cfg.cn = [
8735                 {
8736                     tag : 'i',
8737                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8738                     tooltip : 'This field is required'
8739                 },
8740                 {
8741                     tag: 'label',
8742                     'for' :  id,
8743                     cls : 'control-label',
8744                     html : this.fieldLabel
8745
8746                 },
8747                 {
8748                     cls : "", 
8749                     cn: [
8750                         inputblock
8751                     ]
8752                 }
8753             ];
8754             
8755             var labelCfg = cfg.cn[1];
8756             var contentCfg = cfg.cn[2];
8757             
8758             if(this.indicatorpos == 'right'){
8759                 cfg.cn = [
8760                     {
8761                         tag: 'label',
8762                         'for' :  id,
8763                         cls : 'control-label',
8764                         cn : [
8765                             {
8766                                 tag : 'span',
8767                                 html : this.fieldLabel
8768                             },
8769                             {
8770                                 tag : 'i',
8771                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8772                                 tooltip : 'This field is required'
8773                             }
8774                         ]
8775                     },
8776                     {
8777                         cls : "",
8778                         cn: [
8779                             inputblock
8780                         ]
8781                     }
8782
8783                 ];
8784                 
8785                 labelCfg = cfg.cn[0];
8786                 contentCfg = cfg.cn[1];
8787             
8788             }
8789             
8790             if(this.labelWidth > 12){
8791                 labelCfg.style = "width: " + this.labelWidth + 'px';
8792             }
8793             
8794             if(this.labelWidth < 13 && this.labelmd == 0){
8795                 this.labelmd = this.labelWidth;
8796             }
8797             
8798             if(this.labellg > 0){
8799                 labelCfg.cls += ' col-lg-' + this.labellg;
8800                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8801             }
8802             
8803             if(this.labelmd > 0){
8804                 labelCfg.cls += ' col-md-' + this.labelmd;
8805                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8806             }
8807             
8808             if(this.labelsm > 0){
8809                 labelCfg.cls += ' col-sm-' + this.labelsm;
8810                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8811             }
8812             
8813             if(this.labelxs > 0){
8814                 labelCfg.cls += ' col-xs-' + this.labelxs;
8815                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8816             }
8817             
8818             
8819         } else if ( this.fieldLabel.length) {
8820                 
8821             cfg.cn = [
8822                 {
8823                     tag : 'i',
8824                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8825                     tooltip : 'This field is required'
8826                 },
8827                 {
8828                     tag: 'label',
8829                    //cls : 'input-group-addon',
8830                     html : this.fieldLabel
8831
8832                 },
8833
8834                inputblock
8835
8836            ];
8837            
8838            if(this.indicatorpos == 'right'){
8839                 
8840                 cfg.cn = [
8841                     {
8842                         tag: 'label',
8843                        //cls : 'input-group-addon',
8844                         html : this.fieldLabel
8845
8846                     },
8847                     {
8848                         tag : 'i',
8849                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8850                         tooltip : 'This field is required'
8851                     },
8852
8853                    inputblock
8854
8855                ];
8856
8857             }
8858
8859         } else {
8860             
8861             cfg.cn = [
8862
8863                     inputblock
8864
8865             ];
8866                 
8867                 
8868         };
8869         
8870         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8871            cfg.cls += ' navbar-form';
8872         }
8873         
8874         if (this.parentType === 'NavGroup') {
8875            cfg.cls += ' navbar-form';
8876            cfg.tag = 'li';
8877         }
8878         
8879         return cfg;
8880         
8881     },
8882     /**
8883      * return the real input element.
8884      */
8885     inputEl: function ()
8886     {
8887         return this.el.select('input.form-control',true).first();
8888     },
8889     
8890     tooltipEl : function()
8891     {
8892         return this.inputEl();
8893     },
8894     
8895     indicatorEl : function()
8896     {
8897         var indicator = this.el.select('i.roo-required-indicator',true).first();
8898         
8899         if(!indicator){
8900             return false;
8901         }
8902         
8903         return indicator;
8904         
8905     },
8906     
8907     setDisabled : function(v)
8908     {
8909         var i  = this.inputEl().dom;
8910         if (!v) {
8911             i.removeAttribute('disabled');
8912             return;
8913             
8914         }
8915         i.setAttribute('disabled','true');
8916     },
8917     initEvents : function()
8918     {
8919           
8920         this.inputEl().on("keydown" , this.fireKey,  this);
8921         this.inputEl().on("focus", this.onFocus,  this);
8922         this.inputEl().on("blur", this.onBlur,  this);
8923         
8924         this.inputEl().relayEvent('keyup', this);
8925         
8926         this.indicator = this.indicatorEl();
8927         
8928         if(this.indicator){
8929             this.indicator.addClass('invisible');
8930             
8931         }
8932  
8933         // reference to original value for reset
8934         this.originalValue = this.getValue();
8935         //Roo.form.TextField.superclass.initEvents.call(this);
8936         if(this.validationEvent == 'keyup'){
8937             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8938             this.inputEl().on('keyup', this.filterValidation, this);
8939         }
8940         else if(this.validationEvent !== false){
8941             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8942         }
8943         
8944         if(this.selectOnFocus){
8945             this.on("focus", this.preFocus, this);
8946             
8947         }
8948         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8949             this.inputEl().on("keypress", this.filterKeys, this);
8950         } else {
8951             this.inputEl().relayEvent('keypress', this);
8952         }
8953        /* if(this.grow){
8954             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8955             this.el.on("click", this.autoSize,  this);
8956         }
8957         */
8958         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8959             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8960         }
8961         
8962         if (typeof(this.before) == 'object') {
8963             this.before.render(this.el.select('.roo-input-before',true).first());
8964         }
8965         if (typeof(this.after) == 'object') {
8966             this.after.render(this.el.select('.roo-input-after',true).first());
8967         }
8968         
8969         
8970     },
8971     filterValidation : function(e){
8972         if(!e.isNavKeyPress()){
8973             this.validationTask.delay(this.validationDelay);
8974         }
8975     },
8976      /**
8977      * Validates the field value
8978      * @return {Boolean} True if the value is valid, else false
8979      */
8980     validate : function(){
8981         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8982         if(this.disabled || this.validateValue(this.getRawValue())){
8983             this.markValid();
8984             return true;
8985         }
8986         
8987         this.markInvalid();
8988         return false;
8989     },
8990     
8991     
8992     /**
8993      * Validates a value according to the field's validation rules and marks the field as invalid
8994      * if the validation fails
8995      * @param {Mixed} value The value to validate
8996      * @return {Boolean} True if the value is valid, else false
8997      */
8998     validateValue : function(value){
8999         if(value.length < 1)  { // if it's blank
9000             if(this.allowBlank){
9001                 return true;
9002             }            
9003             return this.inputEl().hasClass('hide') ? true : false;
9004         }
9005         
9006         if(value.length < this.minLength){
9007             return false;
9008         }
9009         if(value.length > this.maxLength){
9010             return false;
9011         }
9012         if(this.vtype){
9013             var vt = Roo.form.VTypes;
9014             if(!vt[this.vtype](value, this)){
9015                 return false;
9016             }
9017         }
9018         if(typeof this.validator == "function"){
9019             var msg = this.validator(value);
9020             if(msg !== true){
9021                 return false;
9022             }
9023             if (typeof(msg) == 'string') {
9024                 this.invalidText = msg;
9025             }
9026         }
9027         
9028         if(this.regex && !this.regex.test(value)){
9029             return false;
9030         }
9031         
9032         return true;
9033     },
9034
9035     
9036     
9037      // private
9038     fireKey : function(e){
9039         //Roo.log('field ' + e.getKey());
9040         if(e.isNavKeyPress()){
9041             this.fireEvent("specialkey", this, e);
9042         }
9043     },
9044     focus : function (selectText){
9045         if(this.rendered){
9046             this.inputEl().focus();
9047             if(selectText === true){
9048                 this.inputEl().dom.select();
9049             }
9050         }
9051         return this;
9052     } ,
9053     
9054     onFocus : function(){
9055         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9056            // this.el.addClass(this.focusClass);
9057         }
9058         if(!this.hasFocus){
9059             this.hasFocus = true;
9060             this.startValue = this.getValue();
9061             this.fireEvent("focus", this);
9062         }
9063     },
9064     
9065     beforeBlur : Roo.emptyFn,
9066
9067     
9068     // private
9069     onBlur : function(){
9070         this.beforeBlur();
9071         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9072             //this.el.removeClass(this.focusClass);
9073         }
9074         this.hasFocus = false;
9075         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9076             this.validate();
9077         }
9078         var v = this.getValue();
9079         if(String(v) !== String(this.startValue)){
9080             this.fireEvent('change', this, v, this.startValue);
9081         }
9082         this.fireEvent("blur", this);
9083     },
9084     
9085     /**
9086      * Resets the current field value to the originally loaded value and clears any validation messages
9087      */
9088     reset : function(){
9089         this.setValue(this.originalValue);
9090         this.validate();
9091     },
9092      /**
9093      * Returns the name of the field
9094      * @return {Mixed} name The name field
9095      */
9096     getName: function(){
9097         return this.name;
9098     },
9099      /**
9100      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9101      * @return {Mixed} value The field value
9102      */
9103     getValue : function(){
9104         
9105         var v = this.inputEl().getValue();
9106         
9107         return v;
9108     },
9109     /**
9110      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9111      * @return {Mixed} value The field value
9112      */
9113     getRawValue : function(){
9114         var v = this.inputEl().getValue();
9115         
9116         return v;
9117     },
9118     
9119     /**
9120      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9121      * @param {Mixed} value The value to set
9122      */
9123     setRawValue : function(v){
9124         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9125     },
9126     
9127     selectText : function(start, end){
9128         var v = this.getRawValue();
9129         if(v.length > 0){
9130             start = start === undefined ? 0 : start;
9131             end = end === undefined ? v.length : end;
9132             var d = this.inputEl().dom;
9133             if(d.setSelectionRange){
9134                 d.setSelectionRange(start, end);
9135             }else if(d.createTextRange){
9136                 var range = d.createTextRange();
9137                 range.moveStart("character", start);
9138                 range.moveEnd("character", v.length-end);
9139                 range.select();
9140             }
9141         }
9142     },
9143     
9144     /**
9145      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9146      * @param {Mixed} value The value to set
9147      */
9148     setValue : function(v){
9149         this.value = v;
9150         if(this.rendered){
9151             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9152             this.validate();
9153         }
9154     },
9155     
9156     /*
9157     processValue : function(value){
9158         if(this.stripCharsRe){
9159             var newValue = value.replace(this.stripCharsRe, '');
9160             if(newValue !== value){
9161                 this.setRawValue(newValue);
9162                 return newValue;
9163             }
9164         }
9165         return value;
9166     },
9167   */
9168     preFocus : function(){
9169         
9170         if(this.selectOnFocus){
9171             this.inputEl().dom.select();
9172         }
9173     },
9174     filterKeys : function(e){
9175         var k = e.getKey();
9176         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9177             return;
9178         }
9179         var c = e.getCharCode(), cc = String.fromCharCode(c);
9180         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9181             return;
9182         }
9183         if(!this.maskRe.test(cc)){
9184             e.stopEvent();
9185         }
9186     },
9187      /**
9188      * Clear any invalid styles/messages for this field
9189      */
9190     clearInvalid : function(){
9191         
9192         if(!this.el || this.preventMark){ // not rendered
9193             return;
9194         }
9195         
9196      
9197         this.el.removeClass(this.invalidClass);
9198         
9199         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9200             
9201             var feedback = this.el.select('.form-control-feedback', true).first();
9202             
9203             if(feedback){
9204                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9205             }
9206             
9207         }
9208         
9209         this.fireEvent('valid', this);
9210     },
9211     
9212      /**
9213      * Mark this field as valid
9214      */
9215     markValid : function()
9216     {
9217         if(!this.el  || this.preventMark){ // not rendered...
9218             return;
9219         }
9220         
9221         this.el.removeClass([this.invalidClass, this.validClass]);
9222         
9223         var feedback = this.el.select('.form-control-feedback', true).first();
9224             
9225         if(feedback){
9226             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9227         }
9228
9229         if(this.disabled){
9230             return;
9231         }
9232         
9233         if(this.allowBlank && !this.getRawValue().length){
9234             return;
9235         }
9236         
9237         if(this.indicator){
9238             this.indicator.removeClass('visible');
9239             this.indicator.addClass('invisible');
9240         }
9241         
9242         this.el.addClass(this.validClass);
9243         
9244         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9245             
9246             var feedback = this.el.select('.form-control-feedback', true).first();
9247             
9248             if(feedback){
9249                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9250                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9251             }
9252             
9253         }
9254         
9255         this.fireEvent('valid', this);
9256     },
9257     
9258      /**
9259      * Mark this field as invalid
9260      * @param {String} msg The validation message
9261      */
9262     markInvalid : function(msg)
9263     {
9264         if(!this.el  || this.preventMark){ // not rendered
9265             return;
9266         }
9267         
9268         this.el.removeClass([this.invalidClass, this.validClass]);
9269         
9270         var feedback = this.el.select('.form-control-feedback', true).first();
9271             
9272         if(feedback){
9273             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9274         }
9275
9276         if(this.disabled){
9277             return;
9278         }
9279         
9280         if(this.allowBlank && !this.getRawValue().length){
9281             return;
9282         }
9283         
9284         if(this.indicator){
9285             this.indicator.removeClass('invisible');
9286             this.indicator.addClass('visible');
9287         }
9288         
9289         this.el.addClass(this.invalidClass);
9290         
9291         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9292             
9293             var feedback = this.el.select('.form-control-feedback', true).first();
9294             
9295             if(feedback){
9296                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9297                 
9298                 if(this.getValue().length || this.forceFeedback){
9299                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9300                 }
9301                 
9302             }
9303             
9304         }
9305         
9306         this.fireEvent('invalid', this, msg);
9307     },
9308     // private
9309     SafariOnKeyDown : function(event)
9310     {
9311         // this is a workaround for a password hang bug on chrome/ webkit.
9312         if (this.inputEl().dom.type != 'password') {
9313             return;
9314         }
9315         
9316         var isSelectAll = false;
9317         
9318         if(this.inputEl().dom.selectionEnd > 0){
9319             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9320         }
9321         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9322             event.preventDefault();
9323             this.setValue('');
9324             return;
9325         }
9326         
9327         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9328             
9329             event.preventDefault();
9330             // this is very hacky as keydown always get's upper case.
9331             //
9332             var cc = String.fromCharCode(event.getCharCode());
9333             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9334             
9335         }
9336     },
9337     adjustWidth : function(tag, w){
9338         tag = tag.toLowerCase();
9339         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9340             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9341                 if(tag == 'input'){
9342                     return w + 2;
9343                 }
9344                 if(tag == 'textarea'){
9345                     return w-2;
9346                 }
9347             }else if(Roo.isOpera){
9348                 if(tag == 'input'){
9349                     return w + 2;
9350                 }
9351                 if(tag == 'textarea'){
9352                     return w-2;
9353                 }
9354             }
9355         }
9356         return w;
9357     },
9358     
9359     setFieldLabel : function(v)
9360     {
9361         this.fieldLabel = v;
9362         
9363         if(this.rendered){
9364             this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9365         }
9366     }
9367 });
9368
9369  
9370 /*
9371  * - LGPL
9372  *
9373  * Input
9374  * 
9375  */
9376
9377 /**
9378  * @class Roo.bootstrap.TextArea
9379  * @extends Roo.bootstrap.Input
9380  * Bootstrap TextArea class
9381  * @cfg {Number} cols Specifies the visible width of a text area
9382  * @cfg {Number} rows Specifies the visible number of lines in a text area
9383  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9384  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9385  * @cfg {string} html text
9386  * 
9387  * @constructor
9388  * Create a new TextArea
9389  * @param {Object} config The config object
9390  */
9391
9392 Roo.bootstrap.TextArea = function(config){
9393     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9394    
9395 };
9396
9397 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9398      
9399     cols : false,
9400     rows : 5,
9401     readOnly : false,
9402     warp : 'soft',
9403     resize : false,
9404     value: false,
9405     html: false,
9406     
9407     getAutoCreate : function(){
9408         
9409         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9410         
9411         var id = Roo.id();
9412         
9413         var cfg = {};
9414         
9415         if(this.inputType != 'hidden'){
9416             cfg.cls = 'form-group' //input-group
9417         }
9418         
9419         var input =  {
9420             tag: 'textarea',
9421             id : id,
9422             warp : this.warp,
9423             rows : this.rows,
9424             value : this.value || '',
9425             html: this.html || '',
9426             cls : 'form-control',
9427             placeholder : this.placeholder || '' 
9428             
9429         };
9430         
9431         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9432             input.maxLength = this.maxLength;
9433         }
9434         
9435         if(this.resize){
9436             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9437         }
9438         
9439         if(this.cols){
9440             input.cols = this.cols;
9441         }
9442         
9443         if (this.readOnly) {
9444             input.readonly = true;
9445         }
9446         
9447         if (this.name) {
9448             input.name = this.name;
9449         }
9450         
9451         if (this.size) {
9452             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9453         }
9454         
9455         var settings=this;
9456         ['xs','sm','md','lg'].map(function(size){
9457             if (settings[size]) {
9458                 cfg.cls += ' col-' + size + '-' + settings[size];
9459             }
9460         });
9461         
9462         var inputblock = input;
9463         
9464         if(this.hasFeedback && !this.allowBlank){
9465             
9466             var feedback = {
9467                 tag: 'span',
9468                 cls: 'glyphicon form-control-feedback'
9469             };
9470
9471             inputblock = {
9472                 cls : 'has-feedback',
9473                 cn :  [
9474                     input,
9475                     feedback
9476                 ] 
9477             };  
9478         }
9479         
9480         
9481         if (this.before || this.after) {
9482             
9483             inputblock = {
9484                 cls : 'input-group',
9485                 cn :  [] 
9486             };
9487             if (this.before) {
9488                 inputblock.cn.push({
9489                     tag :'span',
9490                     cls : 'input-group-addon',
9491                     html : this.before
9492                 });
9493             }
9494             
9495             inputblock.cn.push(input);
9496             
9497             if(this.hasFeedback && !this.allowBlank){
9498                 inputblock.cls += ' has-feedback';
9499                 inputblock.cn.push(feedback);
9500             }
9501             
9502             if (this.after) {
9503                 inputblock.cn.push({
9504                     tag :'span',
9505                     cls : 'input-group-addon',
9506                     html : this.after
9507                 });
9508             }
9509             
9510         }
9511         
9512         if (align ==='left' && this.fieldLabel.length) {
9513             cfg.cn = [
9514                 {
9515                     tag: 'label',
9516                     'for' :  id,
9517                     cls : 'control-label',
9518                     html : this.fieldLabel
9519                 },
9520                 {
9521                     cls : "",
9522                     cn: [
9523                         inputblock
9524                     ]
9525                 }
9526
9527             ];
9528             
9529             if(this.labelWidth > 12){
9530                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9531             }
9532
9533             if(this.labelWidth < 13 && this.labelmd == 0){
9534                 this.labelmd = this.labelWidth;
9535             }
9536
9537             if(this.labellg > 0){
9538                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9539                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9540             }
9541
9542             if(this.labelmd > 0){
9543                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9544                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9545             }
9546
9547             if(this.labelsm > 0){
9548                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9549                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9550             }
9551
9552             if(this.labelxs > 0){
9553                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9554                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9555             }
9556             
9557         } else if ( this.fieldLabel.length) {
9558             cfg.cn = [
9559
9560                {
9561                    tag: 'label',
9562                    //cls : 'input-group-addon',
9563                    html : this.fieldLabel
9564
9565                },
9566
9567                inputblock
9568
9569            ];
9570
9571         } else {
9572
9573             cfg.cn = [
9574
9575                 inputblock
9576
9577             ];
9578                 
9579         }
9580         
9581         if (this.disabled) {
9582             input.disabled=true;
9583         }
9584         
9585         return cfg;
9586         
9587     },
9588     /**
9589      * return the real textarea element.
9590      */
9591     inputEl: function ()
9592     {
9593         return this.el.select('textarea.form-control',true).first();
9594     },
9595     
9596     /**
9597      * Clear any invalid styles/messages for this field
9598      */
9599     clearInvalid : function()
9600     {
9601         
9602         if(!this.el || this.preventMark){ // not rendered
9603             return;
9604         }
9605         
9606         var label = this.el.select('label', true).first();
9607         var icon = this.el.select('i.fa-star', true).first();
9608         
9609         if(label && icon){
9610             icon.remove();
9611         }
9612         
9613         this.el.removeClass(this.invalidClass);
9614         
9615         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9616             
9617             var feedback = this.el.select('.form-control-feedback', true).first();
9618             
9619             if(feedback){
9620                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9621             }
9622             
9623         }
9624         
9625         this.fireEvent('valid', this);
9626     },
9627     
9628      /**
9629      * Mark this field as valid
9630      */
9631     markValid : function()
9632     {
9633         if(!this.el  || this.preventMark){ // not rendered
9634             return;
9635         }
9636         
9637         this.el.removeClass([this.invalidClass, this.validClass]);
9638         
9639         var feedback = this.el.select('.form-control-feedback', true).first();
9640             
9641         if(feedback){
9642             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9643         }
9644
9645         if(this.disabled || this.allowBlank){
9646             return;
9647         }
9648         
9649         var label = this.el.select('label', true).first();
9650         var icon = this.el.select('i.fa-star', true).first();
9651         
9652         if(label && icon){
9653             icon.remove();
9654         }
9655         
9656         this.el.addClass(this.validClass);
9657         
9658         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9659             
9660             var feedback = this.el.select('.form-control-feedback', true).first();
9661             
9662             if(feedback){
9663                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9664                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9665             }
9666             
9667         }
9668         
9669         this.fireEvent('valid', this);
9670     },
9671     
9672      /**
9673      * Mark this field as invalid
9674      * @param {String} msg The validation message
9675      */
9676     markInvalid : function(msg)
9677     {
9678         if(!this.el  || this.preventMark){ // not rendered
9679             return;
9680         }
9681         
9682         this.el.removeClass([this.invalidClass, this.validClass]);
9683         
9684         var feedback = this.el.select('.form-control-feedback', true).first();
9685             
9686         if(feedback){
9687             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9688         }
9689
9690         if(this.disabled || this.allowBlank){
9691             return;
9692         }
9693         
9694         var label = this.el.select('label', true).first();
9695         var icon = this.el.select('i.fa-star', true).first();
9696         
9697         if(!this.getValue().length && label && !icon){
9698             this.el.createChild({
9699                 tag : 'i',
9700                 cls : 'text-danger fa fa-lg fa-star',
9701                 tooltip : 'This field is required',
9702                 style : 'margin-right:5px;'
9703             }, label, true);
9704         }
9705
9706         this.el.addClass(this.invalidClass);
9707         
9708         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9709             
9710             var feedback = this.el.select('.form-control-feedback', true).first();
9711             
9712             if(feedback){
9713                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9714                 
9715                 if(this.getValue().length || this.forceFeedback){
9716                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9717                 }
9718                 
9719             }
9720             
9721         }
9722         
9723         this.fireEvent('invalid', this, msg);
9724     }
9725 });
9726
9727  
9728 /*
9729  * - LGPL
9730  *
9731  * trigger field - base class for combo..
9732  * 
9733  */
9734  
9735 /**
9736  * @class Roo.bootstrap.TriggerField
9737  * @extends Roo.bootstrap.Input
9738  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9739  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9740  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9741  * for which you can provide a custom implementation.  For example:
9742  * <pre><code>
9743 var trigger = new Roo.bootstrap.TriggerField();
9744 trigger.onTriggerClick = myTriggerFn;
9745 trigger.applyTo('my-field');
9746 </code></pre>
9747  *
9748  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9749  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9750  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9751  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9752  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9753
9754  * @constructor
9755  * Create a new TriggerField.
9756  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9757  * to the base TextField)
9758  */
9759 Roo.bootstrap.TriggerField = function(config){
9760     this.mimicing = false;
9761     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9762 };
9763
9764 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9765     /**
9766      * @cfg {String} triggerClass A CSS class to apply to the trigger
9767      */
9768      /**
9769      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9770      */
9771     hideTrigger:false,
9772
9773     /**
9774      * @cfg {Boolean} removable (true|false) special filter default false
9775      */
9776     removable : false,
9777     
9778     /** @cfg {Boolean} grow @hide */
9779     /** @cfg {Number} growMin @hide */
9780     /** @cfg {Number} growMax @hide */
9781
9782     /**
9783      * @hide 
9784      * @method
9785      */
9786     autoSize: Roo.emptyFn,
9787     // private
9788     monitorTab : true,
9789     // private
9790     deferHeight : true,
9791
9792     
9793     actionMode : 'wrap',
9794     
9795     caret : false,
9796     
9797     
9798     getAutoCreate : function(){
9799        
9800         var align = this.labelAlign || this.parentLabelAlign();
9801         
9802         var id = Roo.id();
9803         
9804         var cfg = {
9805             cls: 'form-group' //input-group
9806         };
9807         
9808         
9809         var input =  {
9810             tag: 'input',
9811             id : id,
9812             type : this.inputType,
9813             cls : 'form-control',
9814             autocomplete: 'new-password',
9815             placeholder : this.placeholder || '' 
9816             
9817         };
9818         if (this.name) {
9819             input.name = this.name;
9820         }
9821         if (this.size) {
9822             input.cls += ' input-' + this.size;
9823         }
9824         
9825         if (this.disabled) {
9826             input.disabled=true;
9827         }
9828         
9829         var inputblock = input;
9830         
9831         if(this.hasFeedback && !this.allowBlank){
9832             
9833             var feedback = {
9834                 tag: 'span',
9835                 cls: 'glyphicon form-control-feedback'
9836             };
9837             
9838             if(this.removable && !this.editable && !this.tickable){
9839                 inputblock = {
9840                     cls : 'has-feedback',
9841                     cn :  [
9842                         inputblock,
9843                         {
9844                             tag: 'button',
9845                             html : 'x',
9846                             cls : 'roo-combo-removable-btn close'
9847                         },
9848                         feedback
9849                     ] 
9850                 };
9851             } else {
9852                 inputblock = {
9853                     cls : 'has-feedback',
9854                     cn :  [
9855                         inputblock,
9856                         feedback
9857                     ] 
9858                 };
9859             }
9860
9861         } else {
9862             if(this.removable && !this.editable && !this.tickable){
9863                 inputblock = {
9864                     cls : 'roo-removable',
9865                     cn :  [
9866                         inputblock,
9867                         {
9868                             tag: 'button',
9869                             html : 'x',
9870                             cls : 'roo-combo-removable-btn close'
9871                         }
9872                     ] 
9873                 };
9874             }
9875         }
9876         
9877         if (this.before || this.after) {
9878             
9879             inputblock = {
9880                 cls : 'input-group',
9881                 cn :  [] 
9882             };
9883             if (this.before) {
9884                 inputblock.cn.push({
9885                     tag :'span',
9886                     cls : 'input-group-addon',
9887                     html : this.before
9888                 });
9889             }
9890             
9891             inputblock.cn.push(input);
9892             
9893             if(this.hasFeedback && !this.allowBlank){
9894                 inputblock.cls += ' has-feedback';
9895                 inputblock.cn.push(feedback);
9896             }
9897             
9898             if (this.after) {
9899                 inputblock.cn.push({
9900                     tag :'span',
9901                     cls : 'input-group-addon',
9902                     html : this.after
9903                 });
9904             }
9905             
9906         };
9907         
9908         var box = {
9909             tag: 'div',
9910             cn: [
9911                 {
9912                     tag: 'input',
9913                     type : 'hidden',
9914                     cls: 'form-hidden-field'
9915                 },
9916                 inputblock
9917             ]
9918             
9919         };
9920         
9921         if(this.multiple){
9922             box = {
9923                 tag: 'div',
9924                 cn: [
9925                     {
9926                         tag: 'input',
9927                         type : 'hidden',
9928                         cls: 'form-hidden-field'
9929                     },
9930                     {
9931                         tag: 'ul',
9932                         cls: 'roo-select2-choices',
9933                         cn:[
9934                             {
9935                                 tag: 'li',
9936                                 cls: 'roo-select2-search-field',
9937                                 cn: [
9938
9939                                     inputblock
9940                                 ]
9941                             }
9942                         ]
9943                     }
9944                 ]
9945             }
9946         };
9947         
9948         var combobox = {
9949             cls: 'roo-select2-container input-group',
9950             cn: [
9951                 box
9952 //                {
9953 //                    tag: 'ul',
9954 //                    cls: 'typeahead typeahead-long dropdown-menu',
9955 //                    style: 'display:none'
9956 //                }
9957             ]
9958         };
9959         
9960         if(!this.multiple && this.showToggleBtn){
9961             
9962             var caret = {
9963                         tag: 'span',
9964                         cls: 'caret'
9965              };
9966             if (this.caret != false) {
9967                 caret = {
9968                      tag: 'i',
9969                      cls: 'fa fa-' + this.caret
9970                 };
9971                 
9972             }
9973             
9974             combobox.cn.push({
9975                 tag :'span',
9976                 cls : 'input-group-addon btn dropdown-toggle',
9977                 cn : [
9978                     caret,
9979                     {
9980                         tag: 'span',
9981                         cls: 'combobox-clear',
9982                         cn  : [
9983                             {
9984                                 tag : 'i',
9985                                 cls: 'icon-remove'
9986                             }
9987                         ]
9988                     }
9989                 ]
9990
9991             })
9992         }
9993         
9994         if(this.multiple){
9995             combobox.cls += ' roo-select2-container-multi';
9996         }
9997         
9998         if (align ==='left' && this.fieldLabel.length) {
9999             
10000             cfg.cls += ' roo-form-group-label-left';
10001
10002             cfg.cn = [
10003                 {
10004                     tag : 'i',
10005                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10006                     tooltip : 'This field is required'
10007                 },
10008                 {
10009                     tag: 'label',
10010                     'for' :  id,
10011                     cls : 'control-label',
10012                     html : this.fieldLabel
10013
10014                 },
10015                 {
10016                     cls : "", 
10017                     cn: [
10018                         combobox
10019                     ]
10020                 }
10021
10022             ];
10023             
10024             var labelCfg = cfg.cn[1];
10025             var contentCfg = cfg.cn[2];
10026             
10027             if(this.indicatorpos == 'right'){
10028                 cfg.cn = [
10029                     {
10030                         tag: 'label',
10031                         'for' :  id,
10032                         cls : 'control-label',
10033                         cn : [
10034                             {
10035                                 tag : 'span',
10036                                 html : this.fieldLabel
10037                             },
10038                             {
10039                                 tag : 'i',
10040                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10041                                 tooltip : 'This field is required'
10042                             }
10043                         ]
10044                     },
10045                     {
10046                         cls : "", 
10047                         cn: [
10048                             combobox
10049                         ]
10050                     }
10051
10052                 ];
10053                 
10054                 labelCfg = cfg.cn[0];
10055                 contentCfg = cfg.cn[1];
10056             }
10057             
10058             if(this.labelWidth > 12){
10059                 labelCfg.style = "width: " + this.labelWidth + 'px';
10060             }
10061             
10062             if(this.labelWidth < 13 && this.labelmd == 0){
10063                 this.labelmd = this.labelWidth;
10064             }
10065             
10066             if(this.labellg > 0){
10067                 labelCfg.cls += ' col-lg-' + this.labellg;
10068                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10069             }
10070             
10071             if(this.labelmd > 0){
10072                 labelCfg.cls += ' col-md-' + this.labelmd;
10073                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10074             }
10075             
10076             if(this.labelsm > 0){
10077                 labelCfg.cls += ' col-sm-' + this.labelsm;
10078                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10079             }
10080             
10081             if(this.labelxs > 0){
10082                 labelCfg.cls += ' col-xs-' + this.labelxs;
10083                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10084             }
10085             
10086         } else if ( this.fieldLabel.length) {
10087 //                Roo.log(" label");
10088             cfg.cn = [
10089                 {
10090                    tag : 'i',
10091                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10092                    tooltip : 'This field is required'
10093                },
10094                {
10095                    tag: 'label',
10096                    //cls : 'input-group-addon',
10097                    html : this.fieldLabel
10098
10099                },
10100
10101                combobox
10102
10103             ];
10104             
10105             if(this.indicatorpos == 'right'){
10106                 
10107                 cfg.cn = [
10108                     {
10109                        tag: 'label',
10110                        cn : [
10111                            {
10112                                tag : 'span',
10113                                html : this.fieldLabel
10114                            },
10115                            {
10116                               tag : 'i',
10117                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10118                               tooltip : 'This field is required'
10119                            }
10120                        ]
10121
10122                     },
10123                     combobox
10124
10125                 ];
10126
10127             }
10128
10129         } else {
10130             
10131 //                Roo.log(" no label && no align");
10132                 cfg = combobox
10133                      
10134                 
10135         }
10136         
10137         var settings=this;
10138         ['xs','sm','md','lg'].map(function(size){
10139             if (settings[size]) {
10140                 cfg.cls += ' col-' + size + '-' + settings[size];
10141             }
10142         });
10143         
10144         return cfg;
10145         
10146     },
10147     
10148     
10149     
10150     // private
10151     onResize : function(w, h){
10152 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10153 //        if(typeof w == 'number'){
10154 //            var x = w - this.trigger.getWidth();
10155 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10156 //            this.trigger.setStyle('left', x+'px');
10157 //        }
10158     },
10159
10160     // private
10161     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10162
10163     // private
10164     getResizeEl : function(){
10165         return this.inputEl();
10166     },
10167
10168     // private
10169     getPositionEl : function(){
10170         return this.inputEl();
10171     },
10172
10173     // private
10174     alignErrorIcon : function(){
10175         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10176     },
10177
10178     // private
10179     initEvents : function(){
10180         
10181         this.createList();
10182         
10183         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10184         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10185         if(!this.multiple && this.showToggleBtn){
10186             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10187             if(this.hideTrigger){
10188                 this.trigger.setDisplayed(false);
10189             }
10190             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10191         }
10192         
10193         if(this.multiple){
10194             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10195         }
10196         
10197         if(this.removable && !this.editable && !this.tickable){
10198             var close = this.closeTriggerEl();
10199             
10200             if(close){
10201                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10202                 close.on('click', this.removeBtnClick, this, close);
10203             }
10204         }
10205         
10206         //this.trigger.addClassOnOver('x-form-trigger-over');
10207         //this.trigger.addClassOnClick('x-form-trigger-click');
10208         
10209         //if(!this.width){
10210         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10211         //}
10212     },
10213     
10214     closeTriggerEl : function()
10215     {
10216         var close = this.el.select('.roo-combo-removable-btn', true).first();
10217         return close ? close : false;
10218     },
10219     
10220     removeBtnClick : function(e, h, el)
10221     {
10222         e.preventDefault();
10223         
10224         if(this.fireEvent("remove", this) !== false){
10225             this.reset();
10226             this.fireEvent("afterremove", this)
10227         }
10228     },
10229     
10230     createList : function()
10231     {
10232         this.list = Roo.get(document.body).createChild({
10233             tag: 'ul',
10234             cls: 'typeahead typeahead-long dropdown-menu',
10235             style: 'display:none'
10236         });
10237         
10238         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10239         
10240     },
10241
10242     // private
10243     initTrigger : function(){
10244        
10245     },
10246
10247     // private
10248     onDestroy : function(){
10249         if(this.trigger){
10250             this.trigger.removeAllListeners();
10251           //  this.trigger.remove();
10252         }
10253         //if(this.wrap){
10254         //    this.wrap.remove();
10255         //}
10256         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10257     },
10258
10259     // private
10260     onFocus : function(){
10261         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10262         /*
10263         if(!this.mimicing){
10264             this.wrap.addClass('x-trigger-wrap-focus');
10265             this.mimicing = true;
10266             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10267             if(this.monitorTab){
10268                 this.el.on("keydown", this.checkTab, this);
10269             }
10270         }
10271         */
10272     },
10273
10274     // private
10275     checkTab : function(e){
10276         if(e.getKey() == e.TAB){
10277             this.triggerBlur();
10278         }
10279     },
10280
10281     // private
10282     onBlur : function(){
10283         // do nothing
10284     },
10285
10286     // private
10287     mimicBlur : function(e, t){
10288         /*
10289         if(!this.wrap.contains(t) && this.validateBlur()){
10290             this.triggerBlur();
10291         }
10292         */
10293     },
10294
10295     // private
10296     triggerBlur : function(){
10297         this.mimicing = false;
10298         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10299         if(this.monitorTab){
10300             this.el.un("keydown", this.checkTab, this);
10301         }
10302         //this.wrap.removeClass('x-trigger-wrap-focus');
10303         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10304     },
10305
10306     // private
10307     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10308     validateBlur : function(e, t){
10309         return true;
10310     },
10311
10312     // private
10313     onDisable : function(){
10314         this.inputEl().dom.disabled = true;
10315         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10316         //if(this.wrap){
10317         //    this.wrap.addClass('x-item-disabled');
10318         //}
10319     },
10320
10321     // private
10322     onEnable : function(){
10323         this.inputEl().dom.disabled = false;
10324         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10325         //if(this.wrap){
10326         //    this.el.removeClass('x-item-disabled');
10327         //}
10328     },
10329
10330     // private
10331     onShow : function(){
10332         var ae = this.getActionEl();
10333         
10334         if(ae){
10335             ae.dom.style.display = '';
10336             ae.dom.style.visibility = 'visible';
10337         }
10338     },
10339
10340     // private
10341     
10342     onHide : function(){
10343         var ae = this.getActionEl();
10344         ae.dom.style.display = 'none';
10345     },
10346
10347     /**
10348      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10349      * by an implementing function.
10350      * @method
10351      * @param {EventObject} e
10352      */
10353     onTriggerClick : Roo.emptyFn
10354 });
10355  /*
10356  * Based on:
10357  * Ext JS Library 1.1.1
10358  * Copyright(c) 2006-2007, Ext JS, LLC.
10359  *
10360  * Originally Released Under LGPL - original licence link has changed is not relivant.
10361  *
10362  * Fork - LGPL
10363  * <script type="text/javascript">
10364  */
10365
10366
10367 /**
10368  * @class Roo.data.SortTypes
10369  * @singleton
10370  * Defines the default sorting (casting?) comparison functions used when sorting data.
10371  */
10372 Roo.data.SortTypes = {
10373     /**
10374      * Default sort that does nothing
10375      * @param {Mixed} s The value being converted
10376      * @return {Mixed} The comparison value
10377      */
10378     none : function(s){
10379         return s;
10380     },
10381     
10382     /**
10383      * The regular expression used to strip tags
10384      * @type {RegExp}
10385      * @property
10386      */
10387     stripTagsRE : /<\/?[^>]+>/gi,
10388     
10389     /**
10390      * Strips all HTML tags to sort on text only
10391      * @param {Mixed} s The value being converted
10392      * @return {String} The comparison value
10393      */
10394     asText : function(s){
10395         return String(s).replace(this.stripTagsRE, "");
10396     },
10397     
10398     /**
10399      * Strips all HTML tags to sort on text only - Case insensitive
10400      * @param {Mixed} s The value being converted
10401      * @return {String} The comparison value
10402      */
10403     asUCText : function(s){
10404         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10405     },
10406     
10407     /**
10408      * Case insensitive string
10409      * @param {Mixed} s The value being converted
10410      * @return {String} The comparison value
10411      */
10412     asUCString : function(s) {
10413         return String(s).toUpperCase();
10414     },
10415     
10416     /**
10417      * Date sorting
10418      * @param {Mixed} s The value being converted
10419      * @return {Number} The comparison value
10420      */
10421     asDate : function(s) {
10422         if(!s){
10423             return 0;
10424         }
10425         if(s instanceof Date){
10426             return s.getTime();
10427         }
10428         return Date.parse(String(s));
10429     },
10430     
10431     /**
10432      * Float sorting
10433      * @param {Mixed} s The value being converted
10434      * @return {Float} The comparison value
10435      */
10436     asFloat : function(s) {
10437         var val = parseFloat(String(s).replace(/,/g, ""));
10438         if(isNaN(val)) {
10439             val = 0;
10440         }
10441         return val;
10442     },
10443     
10444     /**
10445      * Integer sorting
10446      * @param {Mixed} s The value being converted
10447      * @return {Number} The comparison value
10448      */
10449     asInt : function(s) {
10450         var val = parseInt(String(s).replace(/,/g, ""));
10451         if(isNaN(val)) {
10452             val = 0;
10453         }
10454         return val;
10455     }
10456 };/*
10457  * Based on:
10458  * Ext JS Library 1.1.1
10459  * Copyright(c) 2006-2007, Ext JS, LLC.
10460  *
10461  * Originally Released Under LGPL - original licence link has changed is not relivant.
10462  *
10463  * Fork - LGPL
10464  * <script type="text/javascript">
10465  */
10466
10467 /**
10468 * @class Roo.data.Record
10469  * Instances of this class encapsulate both record <em>definition</em> information, and record
10470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10471  * to access Records cached in an {@link Roo.data.Store} object.<br>
10472  * <p>
10473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10475  * objects.<br>
10476  * <p>
10477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10478  * @constructor
10479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10480  * {@link #create}. The parameters are the same.
10481  * @param {Array} data An associative Array of data values keyed by the field name.
10482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10484  * not specified an integer id is generated.
10485  */
10486 Roo.data.Record = function(data, id){
10487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10488     this.data = data;
10489 };
10490
10491 /**
10492  * Generate a constructor for a specific record layout.
10493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10495  * Each field definition object may contain the following properties: <ul>
10496  * <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,
10497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10503  * this may be omitted.</p></li>
10504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10505  * <ul><li>auto (Default, implies no conversion)</li>
10506  * <li>string</li>
10507  * <li>int</li>
10508  * <li>float</li>
10509  * <li>boolean</li>
10510  * <li>date</li></ul></p></li>
10511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10514  * by the Reader into an object that will be stored in the Record. It is passed the
10515  * following parameters:<ul>
10516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10517  * </ul></p></li>
10518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10519  * </ul>
10520  * <br>usage:<br><pre><code>
10521 var TopicRecord = Roo.data.Record.create(
10522     {name: 'title', mapping: 'topic_title'},
10523     {name: 'author', mapping: 'username'},
10524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10526     {name: 'lastPoster', mapping: 'user2'},
10527     {name: 'excerpt', mapping: 'post_text'}
10528 );
10529
10530 var myNewRecord = new TopicRecord({
10531     title: 'Do my job please',
10532     author: 'noobie',
10533     totalPosts: 1,
10534     lastPost: new Date(),
10535     lastPoster: 'Animal',
10536     excerpt: 'No way dude!'
10537 });
10538 myStore.add(myNewRecord);
10539 </code></pre>
10540  * @method create
10541  * @static
10542  */
10543 Roo.data.Record.create = function(o){
10544     var f = function(){
10545         f.superclass.constructor.apply(this, arguments);
10546     };
10547     Roo.extend(f, Roo.data.Record);
10548     var p = f.prototype;
10549     p.fields = new Roo.util.MixedCollection(false, function(field){
10550         return field.name;
10551     });
10552     for(var i = 0, len = o.length; i < len; i++){
10553         p.fields.add(new Roo.data.Field(o[i]));
10554     }
10555     f.getField = function(name){
10556         return p.fields.get(name);  
10557     };
10558     return f;
10559 };
10560
10561 Roo.data.Record.AUTO_ID = 1000;
10562 Roo.data.Record.EDIT = 'edit';
10563 Roo.data.Record.REJECT = 'reject';
10564 Roo.data.Record.COMMIT = 'commit';
10565
10566 Roo.data.Record.prototype = {
10567     /**
10568      * Readonly flag - true if this record has been modified.
10569      * @type Boolean
10570      */
10571     dirty : false,
10572     editing : false,
10573     error: null,
10574     modified: null,
10575
10576     // private
10577     join : function(store){
10578         this.store = store;
10579     },
10580
10581     /**
10582      * Set the named field to the specified value.
10583      * @param {String} name The name of the field to set.
10584      * @param {Object} value The value to set the field to.
10585      */
10586     set : function(name, value){
10587         if(this.data[name] == value){
10588             return;
10589         }
10590         this.dirty = true;
10591         if(!this.modified){
10592             this.modified = {};
10593         }
10594         if(typeof this.modified[name] == 'undefined'){
10595             this.modified[name] = this.data[name];
10596         }
10597         this.data[name] = value;
10598         if(!this.editing && this.store){
10599             this.store.afterEdit(this);
10600         }       
10601     },
10602
10603     /**
10604      * Get the value of the named field.
10605      * @param {String} name The name of the field to get the value of.
10606      * @return {Object} The value of the field.
10607      */
10608     get : function(name){
10609         return this.data[name]; 
10610     },
10611
10612     // private
10613     beginEdit : function(){
10614         this.editing = true;
10615         this.modified = {}; 
10616     },
10617
10618     // private
10619     cancelEdit : function(){
10620         this.editing = false;
10621         delete this.modified;
10622     },
10623
10624     // private
10625     endEdit : function(){
10626         this.editing = false;
10627         if(this.dirty && this.store){
10628             this.store.afterEdit(this);
10629         }
10630     },
10631
10632     /**
10633      * Usually called by the {@link Roo.data.Store} which owns the Record.
10634      * Rejects all changes made to the Record since either creation, or the last commit operation.
10635      * Modified fields are reverted to their original values.
10636      * <p>
10637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10638      * of reject operations.
10639      */
10640     reject : function(){
10641         var m = this.modified;
10642         for(var n in m){
10643             if(typeof m[n] != "function"){
10644                 this.data[n] = m[n];
10645             }
10646         }
10647         this.dirty = false;
10648         delete this.modified;
10649         this.editing = false;
10650         if(this.store){
10651             this.store.afterReject(this);
10652         }
10653     },
10654
10655     /**
10656      * Usually called by the {@link Roo.data.Store} which owns the Record.
10657      * Commits all changes made to the Record since either creation, or the last commit operation.
10658      * <p>
10659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10660      * of commit operations.
10661      */
10662     commit : function(){
10663         this.dirty = false;
10664         delete this.modified;
10665         this.editing = false;
10666         if(this.store){
10667             this.store.afterCommit(this);
10668         }
10669     },
10670
10671     // private
10672     hasError : function(){
10673         return this.error != null;
10674     },
10675
10676     // private
10677     clearError : function(){
10678         this.error = null;
10679     },
10680
10681     /**
10682      * Creates a copy of this record.
10683      * @param {String} id (optional) A new record id if you don't want to use this record's id
10684      * @return {Record}
10685      */
10686     copy : function(newId) {
10687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10688     }
10689 };/*
10690  * Based on:
10691  * Ext JS Library 1.1.1
10692  * Copyright(c) 2006-2007, Ext JS, LLC.
10693  *
10694  * Originally Released Under LGPL - original licence link has changed is not relivant.
10695  *
10696  * Fork - LGPL
10697  * <script type="text/javascript">
10698  */
10699
10700
10701
10702 /**
10703  * @class Roo.data.Store
10704  * @extends Roo.util.Observable
10705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10707  * <p>
10708  * 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
10709  * has no knowledge of the format of the data returned by the Proxy.<br>
10710  * <p>
10711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10712  * instances from the data object. These records are cached and made available through accessor functions.
10713  * @constructor
10714  * Creates a new Store.
10715  * @param {Object} config A config object containing the objects needed for the Store to access data,
10716  * and read the data into Records.
10717  */
10718 Roo.data.Store = function(config){
10719     this.data = new Roo.util.MixedCollection(false);
10720     this.data.getKey = function(o){
10721         return o.id;
10722     };
10723     this.baseParams = {};
10724     // private
10725     this.paramNames = {
10726         "start" : "start",
10727         "limit" : "limit",
10728         "sort" : "sort",
10729         "dir" : "dir",
10730         "multisort" : "_multisort"
10731     };
10732
10733     if(config && config.data){
10734         this.inlineData = config.data;
10735         delete config.data;
10736     }
10737
10738     Roo.apply(this, config);
10739     
10740     if(this.reader){ // reader passed
10741         this.reader = Roo.factory(this.reader, Roo.data);
10742         this.reader.xmodule = this.xmodule || false;
10743         if(!this.recordType){
10744             this.recordType = this.reader.recordType;
10745         }
10746         if(this.reader.onMetaChange){
10747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10748         }
10749     }
10750
10751     if(this.recordType){
10752         this.fields = this.recordType.prototype.fields;
10753     }
10754     this.modified = [];
10755
10756     this.addEvents({
10757         /**
10758          * @event datachanged
10759          * Fires when the data cache has changed, and a widget which is using this Store
10760          * as a Record cache should refresh its view.
10761          * @param {Store} this
10762          */
10763         datachanged : true,
10764         /**
10765          * @event metachange
10766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10767          * @param {Store} this
10768          * @param {Object} meta The JSON metadata
10769          */
10770         metachange : true,
10771         /**
10772          * @event add
10773          * Fires when Records have been added to the Store
10774          * @param {Store} this
10775          * @param {Roo.data.Record[]} records The array of Records added
10776          * @param {Number} index The index at which the record(s) were added
10777          */
10778         add : true,
10779         /**
10780          * @event remove
10781          * Fires when a Record has been removed from the Store
10782          * @param {Store} this
10783          * @param {Roo.data.Record} record The Record that was removed
10784          * @param {Number} index The index at which the record was removed
10785          */
10786         remove : true,
10787         /**
10788          * @event update
10789          * Fires when a Record has been updated
10790          * @param {Store} this
10791          * @param {Roo.data.Record} record The Record that was updated
10792          * @param {String} operation The update operation being performed.  Value may be one of:
10793          * <pre><code>
10794  Roo.data.Record.EDIT
10795  Roo.data.Record.REJECT
10796  Roo.data.Record.COMMIT
10797          * </code></pre>
10798          */
10799         update : true,
10800         /**
10801          * @event clear
10802          * Fires when the data cache has been cleared.
10803          * @param {Store} this
10804          */
10805         clear : true,
10806         /**
10807          * @event beforeload
10808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10809          * the load action will be canceled.
10810          * @param {Store} this
10811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10812          */
10813         beforeload : true,
10814         /**
10815          * @event beforeloadadd
10816          * Fires after a new set of Records has been loaded.
10817          * @param {Store} this
10818          * @param {Roo.data.Record[]} records The Records that were loaded
10819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10820          */
10821         beforeloadadd : true,
10822         /**
10823          * @event load
10824          * Fires after a new set of Records has been loaded, before they are added to the store.
10825          * @param {Store} this
10826          * @param {Roo.data.Record[]} records The Records that were loaded
10827          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10828          * @params {Object} return from reader
10829          */
10830         load : true,
10831         /**
10832          * @event loadexception
10833          * Fires if an exception occurs in the Proxy during loading.
10834          * Called with the signature of the Proxy's "loadexception" event.
10835          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10836          * 
10837          * @param {Proxy} 
10838          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10839          * @param {Object} load options 
10840          * @param {Object} jsonData from your request (normally this contains the Exception)
10841          */
10842         loadexception : true
10843     });
10844     
10845     if(this.proxy){
10846         this.proxy = Roo.factory(this.proxy, Roo.data);
10847         this.proxy.xmodule = this.xmodule || false;
10848         this.relayEvents(this.proxy,  ["loadexception"]);
10849     }
10850     this.sortToggle = {};
10851     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10852
10853     Roo.data.Store.superclass.constructor.call(this);
10854
10855     if(this.inlineData){
10856         this.loadData(this.inlineData);
10857         delete this.inlineData;
10858     }
10859 };
10860
10861 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10862      /**
10863     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10864     * without a remote query - used by combo/forms at present.
10865     */
10866     
10867     /**
10868     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10869     */
10870     /**
10871     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10872     */
10873     /**
10874     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10875     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10876     */
10877     /**
10878     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10879     * on any HTTP request
10880     */
10881     /**
10882     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10883     */
10884     /**
10885     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10886     */
10887     multiSort: false,
10888     /**
10889     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10890     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10891     */
10892     remoteSort : false,
10893
10894     /**
10895     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10896      * loaded or when a record is removed. (defaults to false).
10897     */
10898     pruneModifiedRecords : false,
10899
10900     // private
10901     lastOptions : null,
10902
10903     /**
10904      * Add Records to the Store and fires the add event.
10905      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10906      */
10907     add : function(records){
10908         records = [].concat(records);
10909         for(var i = 0, len = records.length; i < len; i++){
10910             records[i].join(this);
10911         }
10912         var index = this.data.length;
10913         this.data.addAll(records);
10914         this.fireEvent("add", this, records, index);
10915     },
10916
10917     /**
10918      * Remove a Record from the Store and fires the remove event.
10919      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10920      */
10921     remove : function(record){
10922         var index = this.data.indexOf(record);
10923         this.data.removeAt(index);
10924         if(this.pruneModifiedRecords){
10925             this.modified.remove(record);
10926         }
10927         this.fireEvent("remove", this, record, index);
10928     },
10929
10930     /**
10931      * Remove all Records from the Store and fires the clear event.
10932      */
10933     removeAll : function(){
10934         this.data.clear();
10935         if(this.pruneModifiedRecords){
10936             this.modified = [];
10937         }
10938         this.fireEvent("clear", this);
10939     },
10940
10941     /**
10942      * Inserts Records to the Store at the given index and fires the add event.
10943      * @param {Number} index The start index at which to insert the passed Records.
10944      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10945      */
10946     insert : function(index, records){
10947         records = [].concat(records);
10948         for(var i = 0, len = records.length; i < len; i++){
10949             this.data.insert(index, records[i]);
10950             records[i].join(this);
10951         }
10952         this.fireEvent("add", this, records, index);
10953     },
10954
10955     /**
10956      * Get the index within the cache of the passed Record.
10957      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10958      * @return {Number} The index of the passed Record. Returns -1 if not found.
10959      */
10960     indexOf : function(record){
10961         return this.data.indexOf(record);
10962     },
10963
10964     /**
10965      * Get the index within the cache of the Record with the passed id.
10966      * @param {String} id The id of the Record to find.
10967      * @return {Number} The index of the Record. Returns -1 if not found.
10968      */
10969     indexOfId : function(id){
10970         return this.data.indexOfKey(id);
10971     },
10972
10973     /**
10974      * Get the Record with the specified id.
10975      * @param {String} id The id of the Record to find.
10976      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10977      */
10978     getById : function(id){
10979         return this.data.key(id);
10980     },
10981
10982     /**
10983      * Get the Record at the specified index.
10984      * @param {Number} index The index of the Record to find.
10985      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10986      */
10987     getAt : function(index){
10988         return this.data.itemAt(index);
10989     },
10990
10991     /**
10992      * Returns a range of Records between specified indices.
10993      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10994      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10995      * @return {Roo.data.Record[]} An array of Records
10996      */
10997     getRange : function(start, end){
10998         return this.data.getRange(start, end);
10999     },
11000
11001     // private
11002     storeOptions : function(o){
11003         o = Roo.apply({}, o);
11004         delete o.callback;
11005         delete o.scope;
11006         this.lastOptions = o;
11007     },
11008
11009     /**
11010      * Loads the Record cache from the configured Proxy using the configured Reader.
11011      * <p>
11012      * If using remote paging, then the first load call must specify the <em>start</em>
11013      * and <em>limit</em> properties in the options.params property to establish the initial
11014      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11015      * <p>
11016      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11017      * and this call will return before the new data has been loaded. Perform any post-processing
11018      * in a callback function, or in a "load" event handler.</strong>
11019      * <p>
11020      * @param {Object} options An object containing properties which control loading options:<ul>
11021      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11022      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11023      * passed the following arguments:<ul>
11024      * <li>r : Roo.data.Record[]</li>
11025      * <li>options: Options object from the load call</li>
11026      * <li>success: Boolean success indicator</li></ul></li>
11027      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11028      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11029      * </ul>
11030      */
11031     load : function(options){
11032         options = options || {};
11033         if(this.fireEvent("beforeload", this, options) !== false){
11034             this.storeOptions(options);
11035             var p = Roo.apply(options.params || {}, this.baseParams);
11036             // if meta was not loaded from remote source.. try requesting it.
11037             if (!this.reader.metaFromRemote) {
11038                 p._requestMeta = 1;
11039             }
11040             if(this.sortInfo && this.remoteSort){
11041                 var pn = this.paramNames;
11042                 p[pn["sort"]] = this.sortInfo.field;
11043                 p[pn["dir"]] = this.sortInfo.direction;
11044             }
11045             if (this.multiSort) {
11046                 var pn = this.paramNames;
11047                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11048             }
11049             
11050             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11051         }
11052     },
11053
11054     /**
11055      * Reloads the Record cache from the configured Proxy using the configured Reader and
11056      * the options from the last load operation performed.
11057      * @param {Object} options (optional) An object containing properties which may override the options
11058      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11059      * the most recently used options are reused).
11060      */
11061     reload : function(options){
11062         this.load(Roo.applyIf(options||{}, this.lastOptions));
11063     },
11064
11065     // private
11066     // Called as a callback by the Reader during a load operation.
11067     loadRecords : function(o, options, success){
11068         if(!o || success === false){
11069             if(success !== false){
11070                 this.fireEvent("load", this, [], options, o);
11071             }
11072             if(options.callback){
11073                 options.callback.call(options.scope || this, [], options, false);
11074             }
11075             return;
11076         }
11077         // if data returned failure - throw an exception.
11078         if (o.success === false) {
11079             // show a message if no listener is registered.
11080             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11081                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11082             }
11083             // loadmask wil be hooked into this..
11084             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11085             return;
11086         }
11087         var r = o.records, t = o.totalRecords || r.length;
11088         
11089         this.fireEvent("beforeloadadd", this, r, options, o);
11090         
11091         if(!options || options.add !== true){
11092             if(this.pruneModifiedRecords){
11093                 this.modified = [];
11094             }
11095             for(var i = 0, len = r.length; i < len; i++){
11096                 r[i].join(this);
11097             }
11098             if(this.snapshot){
11099                 this.data = this.snapshot;
11100                 delete this.snapshot;
11101             }
11102             this.data.clear();
11103             this.data.addAll(r);
11104             this.totalLength = t;
11105             this.applySort();
11106             this.fireEvent("datachanged", this);
11107         }else{
11108             this.totalLength = Math.max(t, this.data.length+r.length);
11109             this.add(r);
11110         }
11111         
11112         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11113                 
11114             var e = new Roo.data.Record({});
11115
11116             e.set(this.parent.displayField, this.parent.emptyTitle);
11117             e.set(this.parent.valueField, '');
11118
11119             this.insert(0, e);
11120         }
11121             
11122         this.fireEvent("load", this, r, options, o);
11123         if(options.callback){
11124             options.callback.call(options.scope || this, r, options, true);
11125         }
11126     },
11127
11128
11129     /**
11130      * Loads data from a passed data block. A Reader which understands the format of the data
11131      * must have been configured in the constructor.
11132      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11133      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11134      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11135      */
11136     loadData : function(o, append){
11137         var r = this.reader.readRecords(o);
11138         this.loadRecords(r, {add: append}, true);
11139     },
11140
11141     /**
11142      * Gets the number of cached records.
11143      * <p>
11144      * <em>If using paging, this may not be the total size of the dataset. If the data object
11145      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11146      * the data set size</em>
11147      */
11148     getCount : function(){
11149         return this.data.length || 0;
11150     },
11151
11152     /**
11153      * Gets the total number of records in the dataset as returned by the server.
11154      * <p>
11155      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11156      * the dataset size</em>
11157      */
11158     getTotalCount : function(){
11159         return this.totalLength || 0;
11160     },
11161
11162     /**
11163      * Returns the sort state of the Store as an object with two properties:
11164      * <pre><code>
11165  field {String} The name of the field by which the Records are sorted
11166  direction {String} The sort order, "ASC" or "DESC"
11167      * </code></pre>
11168      */
11169     getSortState : function(){
11170         return this.sortInfo;
11171     },
11172
11173     // private
11174     applySort : function(){
11175         if(this.sortInfo && !this.remoteSort){
11176             var s = this.sortInfo, f = s.field;
11177             var st = this.fields.get(f).sortType;
11178             var fn = function(r1, r2){
11179                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11180                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11181             };
11182             this.data.sort(s.direction, fn);
11183             if(this.snapshot && this.snapshot != this.data){
11184                 this.snapshot.sort(s.direction, fn);
11185             }
11186         }
11187     },
11188
11189     /**
11190      * Sets the default sort column and order to be used by the next load operation.
11191      * @param {String} fieldName The name of the field to sort by.
11192      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11193      */
11194     setDefaultSort : function(field, dir){
11195         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11196     },
11197
11198     /**
11199      * Sort the Records.
11200      * If remote sorting is used, the sort is performed on the server, and the cache is
11201      * reloaded. If local sorting is used, the cache is sorted internally.
11202      * @param {String} fieldName The name of the field to sort by.
11203      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11204      */
11205     sort : function(fieldName, dir){
11206         var f = this.fields.get(fieldName);
11207         if(!dir){
11208             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11209             
11210             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11211                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11212             }else{
11213                 dir = f.sortDir;
11214             }
11215         }
11216         this.sortToggle[f.name] = dir;
11217         this.sortInfo = {field: f.name, direction: dir};
11218         if(!this.remoteSort){
11219             this.applySort();
11220             this.fireEvent("datachanged", this);
11221         }else{
11222             this.load(this.lastOptions);
11223         }
11224     },
11225
11226     /**
11227      * Calls the specified function for each of the Records in the cache.
11228      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11229      * Returning <em>false</em> aborts and exits the iteration.
11230      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11231      */
11232     each : function(fn, scope){
11233         this.data.each(fn, scope);
11234     },
11235
11236     /**
11237      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11238      * (e.g., during paging).
11239      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11240      */
11241     getModifiedRecords : function(){
11242         return this.modified;
11243     },
11244
11245     // private
11246     createFilterFn : function(property, value, anyMatch){
11247         if(!value.exec){ // not a regex
11248             value = String(value);
11249             if(value.length == 0){
11250                 return false;
11251             }
11252             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11253         }
11254         return function(r){
11255             return value.test(r.data[property]);
11256         };
11257     },
11258
11259     /**
11260      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11261      * @param {String} property A field on your records
11262      * @param {Number} start The record index to start at (defaults to 0)
11263      * @param {Number} end The last record index to include (defaults to length - 1)
11264      * @return {Number} The sum
11265      */
11266     sum : function(property, start, end){
11267         var rs = this.data.items, v = 0;
11268         start = start || 0;
11269         end = (end || end === 0) ? end : rs.length-1;
11270
11271         for(var i = start; i <= end; i++){
11272             v += (rs[i].data[property] || 0);
11273         }
11274         return v;
11275     },
11276
11277     /**
11278      * Filter the records by a specified property.
11279      * @param {String} field A field on your records
11280      * @param {String/RegExp} value Either a string that the field
11281      * should start with or a RegExp to test against the field
11282      * @param {Boolean} anyMatch True to match any part not just the beginning
11283      */
11284     filter : function(property, value, anyMatch){
11285         var fn = this.createFilterFn(property, value, anyMatch);
11286         return fn ? this.filterBy(fn) : this.clearFilter();
11287     },
11288
11289     /**
11290      * Filter by a function. The specified function will be called with each
11291      * record in this data source. If the function returns true the record is included,
11292      * otherwise it is filtered.
11293      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11294      * @param {Object} scope (optional) The scope of the function (defaults to this)
11295      */
11296     filterBy : function(fn, scope){
11297         this.snapshot = this.snapshot || this.data;
11298         this.data = this.queryBy(fn, scope||this);
11299         this.fireEvent("datachanged", this);
11300     },
11301
11302     /**
11303      * Query the records by a specified property.
11304      * @param {String} field A field on your records
11305      * @param {String/RegExp} value Either a string that the field
11306      * should start with or a RegExp to test against the field
11307      * @param {Boolean} anyMatch True to match any part not just the beginning
11308      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11309      */
11310     query : function(property, value, anyMatch){
11311         var fn = this.createFilterFn(property, value, anyMatch);
11312         return fn ? this.queryBy(fn) : this.data.clone();
11313     },
11314
11315     /**
11316      * Query by a function. The specified function will be called with each
11317      * record in this data source. If the function returns true the record is included
11318      * in the results.
11319      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11320      * @param {Object} scope (optional) The scope of the function (defaults to this)
11321       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11322      **/
11323     queryBy : function(fn, scope){
11324         var data = this.snapshot || this.data;
11325         return data.filterBy(fn, scope||this);
11326     },
11327
11328     /**
11329      * Collects unique values for a particular dataIndex from this store.
11330      * @param {String} dataIndex The property to collect
11331      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11332      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11333      * @return {Array} An array of the unique values
11334      **/
11335     collect : function(dataIndex, allowNull, bypassFilter){
11336         var d = (bypassFilter === true && this.snapshot) ?
11337                 this.snapshot.items : this.data.items;
11338         var v, sv, r = [], l = {};
11339         for(var i = 0, len = d.length; i < len; i++){
11340             v = d[i].data[dataIndex];
11341             sv = String(v);
11342             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11343                 l[sv] = true;
11344                 r[r.length] = v;
11345             }
11346         }
11347         return r;
11348     },
11349
11350     /**
11351      * Revert to a view of the Record cache with no filtering applied.
11352      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11353      */
11354     clearFilter : function(suppressEvent){
11355         if(this.snapshot && this.snapshot != this.data){
11356             this.data = this.snapshot;
11357             delete this.snapshot;
11358             if(suppressEvent !== true){
11359                 this.fireEvent("datachanged", this);
11360             }
11361         }
11362     },
11363
11364     // private
11365     afterEdit : function(record){
11366         if(this.modified.indexOf(record) == -1){
11367             this.modified.push(record);
11368         }
11369         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11370     },
11371     
11372     // private
11373     afterReject : function(record){
11374         this.modified.remove(record);
11375         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11376     },
11377
11378     // private
11379     afterCommit : function(record){
11380         this.modified.remove(record);
11381         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11382     },
11383
11384     /**
11385      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11386      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11387      */
11388     commitChanges : function(){
11389         var m = this.modified.slice(0);
11390         this.modified = [];
11391         for(var i = 0, len = m.length; i < len; i++){
11392             m[i].commit();
11393         }
11394     },
11395
11396     /**
11397      * Cancel outstanding changes on all changed records.
11398      */
11399     rejectChanges : function(){
11400         var m = this.modified.slice(0);
11401         this.modified = [];
11402         for(var i = 0, len = m.length; i < len; i++){
11403             m[i].reject();
11404         }
11405     },
11406
11407     onMetaChange : function(meta, rtype, o){
11408         this.recordType = rtype;
11409         this.fields = rtype.prototype.fields;
11410         delete this.snapshot;
11411         this.sortInfo = meta.sortInfo || this.sortInfo;
11412         this.modified = [];
11413         this.fireEvent('metachange', this, this.reader.meta);
11414     },
11415     
11416     moveIndex : function(data, type)
11417     {
11418         var index = this.indexOf(data);
11419         
11420         var newIndex = index + type;
11421         
11422         this.remove(data);
11423         
11424         this.insert(newIndex, data);
11425         
11426     }
11427 });/*
11428  * Based on:
11429  * Ext JS Library 1.1.1
11430  * Copyright(c) 2006-2007, Ext JS, LLC.
11431  *
11432  * Originally Released Under LGPL - original licence link has changed is not relivant.
11433  *
11434  * Fork - LGPL
11435  * <script type="text/javascript">
11436  */
11437
11438 /**
11439  * @class Roo.data.SimpleStore
11440  * @extends Roo.data.Store
11441  * Small helper class to make creating Stores from Array data easier.
11442  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11443  * @cfg {Array} fields An array of field definition objects, or field name strings.
11444  * @cfg {Array} data The multi-dimensional array of data
11445  * @constructor
11446  * @param {Object} config
11447  */
11448 Roo.data.SimpleStore = function(config){
11449     Roo.data.SimpleStore.superclass.constructor.call(this, {
11450         isLocal : true,
11451         reader: new Roo.data.ArrayReader({
11452                 id: config.id
11453             },
11454             Roo.data.Record.create(config.fields)
11455         ),
11456         proxy : new Roo.data.MemoryProxy(config.data)
11457     });
11458     this.load();
11459 };
11460 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11461  * Based on:
11462  * Ext JS Library 1.1.1
11463  * Copyright(c) 2006-2007, Ext JS, LLC.
11464  *
11465  * Originally Released Under LGPL - original licence link has changed is not relivant.
11466  *
11467  * Fork - LGPL
11468  * <script type="text/javascript">
11469  */
11470
11471 /**
11472 /**
11473  * @extends Roo.data.Store
11474  * @class Roo.data.JsonStore
11475  * Small helper class to make creating Stores for JSON data easier. <br/>
11476 <pre><code>
11477 var store = new Roo.data.JsonStore({
11478     url: 'get-images.php',
11479     root: 'images',
11480     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11481 });
11482 </code></pre>
11483  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11484  * JsonReader and HttpProxy (unless inline data is provided).</b>
11485  * @cfg {Array} fields An array of field definition objects, or field name strings.
11486  * @constructor
11487  * @param {Object} config
11488  */
11489 Roo.data.JsonStore = function(c){
11490     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11491         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11492         reader: new Roo.data.JsonReader(c, c.fields)
11493     }));
11494 };
11495 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11496  * Based on:
11497  * Ext JS Library 1.1.1
11498  * Copyright(c) 2006-2007, Ext JS, LLC.
11499  *
11500  * Originally Released Under LGPL - original licence link has changed is not relivant.
11501  *
11502  * Fork - LGPL
11503  * <script type="text/javascript">
11504  */
11505
11506  
11507 Roo.data.Field = function(config){
11508     if(typeof config == "string"){
11509         config = {name: config};
11510     }
11511     Roo.apply(this, config);
11512     
11513     if(!this.type){
11514         this.type = "auto";
11515     }
11516     
11517     var st = Roo.data.SortTypes;
11518     // named sortTypes are supported, here we look them up
11519     if(typeof this.sortType == "string"){
11520         this.sortType = st[this.sortType];
11521     }
11522     
11523     // set default sortType for strings and dates
11524     if(!this.sortType){
11525         switch(this.type){
11526             case "string":
11527                 this.sortType = st.asUCString;
11528                 break;
11529             case "date":
11530                 this.sortType = st.asDate;
11531                 break;
11532             default:
11533                 this.sortType = st.none;
11534         }
11535     }
11536
11537     // define once
11538     var stripRe = /[\$,%]/g;
11539
11540     // prebuilt conversion function for this field, instead of
11541     // switching every time we're reading a value
11542     if(!this.convert){
11543         var cv, dateFormat = this.dateFormat;
11544         switch(this.type){
11545             case "":
11546             case "auto":
11547             case undefined:
11548                 cv = function(v){ return v; };
11549                 break;
11550             case "string":
11551                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11552                 break;
11553             case "int":
11554                 cv = function(v){
11555                     return v !== undefined && v !== null && v !== '' ?
11556                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11557                     };
11558                 break;
11559             case "float":
11560                 cv = function(v){
11561                     return v !== undefined && v !== null && v !== '' ?
11562                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11563                     };
11564                 break;
11565             case "bool":
11566             case "boolean":
11567                 cv = function(v){ return v === true || v === "true" || v == 1; };
11568                 break;
11569             case "date":
11570                 cv = function(v){
11571                     if(!v){
11572                         return '';
11573                     }
11574                     if(v instanceof Date){
11575                         return v;
11576                     }
11577                     if(dateFormat){
11578                         if(dateFormat == "timestamp"){
11579                             return new Date(v*1000);
11580                         }
11581                         return Date.parseDate(v, dateFormat);
11582                     }
11583                     var parsed = Date.parse(v);
11584                     return parsed ? new Date(parsed) : null;
11585                 };
11586              break;
11587             
11588         }
11589         this.convert = cv;
11590     }
11591 };
11592
11593 Roo.data.Field.prototype = {
11594     dateFormat: null,
11595     defaultValue: "",
11596     mapping: null,
11597     sortType : null,
11598     sortDir : "ASC"
11599 };/*
11600  * Based on:
11601  * Ext JS Library 1.1.1
11602  * Copyright(c) 2006-2007, Ext JS, LLC.
11603  *
11604  * Originally Released Under LGPL - original licence link has changed is not relivant.
11605  *
11606  * Fork - LGPL
11607  * <script type="text/javascript">
11608  */
11609  
11610 // Base class for reading structured data from a data source.  This class is intended to be
11611 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11612
11613 /**
11614  * @class Roo.data.DataReader
11615  * Base class for reading structured data from a data source.  This class is intended to be
11616  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11617  */
11618
11619 Roo.data.DataReader = function(meta, recordType){
11620     
11621     this.meta = meta;
11622     
11623     this.recordType = recordType instanceof Array ? 
11624         Roo.data.Record.create(recordType) : recordType;
11625 };
11626
11627 Roo.data.DataReader.prototype = {
11628      /**
11629      * Create an empty record
11630      * @param {Object} data (optional) - overlay some values
11631      * @return {Roo.data.Record} record created.
11632      */
11633     newRow :  function(d) {
11634         var da =  {};
11635         this.recordType.prototype.fields.each(function(c) {
11636             switch( c.type) {
11637                 case 'int' : da[c.name] = 0; break;
11638                 case 'date' : da[c.name] = new Date(); break;
11639                 case 'float' : da[c.name] = 0.0; break;
11640                 case 'boolean' : da[c.name] = false; break;
11641                 default : da[c.name] = ""; break;
11642             }
11643             
11644         });
11645         return new this.recordType(Roo.apply(da, d));
11646     }
11647     
11648 };/*
11649  * Based on:
11650  * Ext JS Library 1.1.1
11651  * Copyright(c) 2006-2007, Ext JS, LLC.
11652  *
11653  * Originally Released Under LGPL - original licence link has changed is not relivant.
11654  *
11655  * Fork - LGPL
11656  * <script type="text/javascript">
11657  */
11658
11659 /**
11660  * @class Roo.data.DataProxy
11661  * @extends Roo.data.Observable
11662  * This class is an abstract base class for implementations which provide retrieval of
11663  * unformatted data objects.<br>
11664  * <p>
11665  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11666  * (of the appropriate type which knows how to parse the data object) to provide a block of
11667  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11668  * <p>
11669  * Custom implementations must implement the load method as described in
11670  * {@link Roo.data.HttpProxy#load}.
11671  */
11672 Roo.data.DataProxy = function(){
11673     this.addEvents({
11674         /**
11675          * @event beforeload
11676          * Fires before a network request is made to retrieve a data object.
11677          * @param {Object} This DataProxy object.
11678          * @param {Object} params The params parameter to the load function.
11679          */
11680         beforeload : true,
11681         /**
11682          * @event load
11683          * Fires before the load method's callback is called.
11684          * @param {Object} This DataProxy object.
11685          * @param {Object} o The data object.
11686          * @param {Object} arg The callback argument object passed to the load function.
11687          */
11688         load : true,
11689         /**
11690          * @event loadexception
11691          * Fires if an Exception occurs during data retrieval.
11692          * @param {Object} This DataProxy object.
11693          * @param {Object} o The data object.
11694          * @param {Object} arg The callback argument object passed to the load function.
11695          * @param {Object} e The Exception.
11696          */
11697         loadexception : true
11698     });
11699     Roo.data.DataProxy.superclass.constructor.call(this);
11700 };
11701
11702 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11703
11704     /**
11705      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11706      */
11707 /*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717 /**
11718  * @class Roo.data.MemoryProxy
11719  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11720  * to the Reader when its load method is called.
11721  * @constructor
11722  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11723  */
11724 Roo.data.MemoryProxy = function(data){
11725     if (data.data) {
11726         data = data.data;
11727     }
11728     Roo.data.MemoryProxy.superclass.constructor.call(this);
11729     this.data = data;
11730 };
11731
11732 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11733     
11734     /**
11735      * Load data from the requested source (in this case an in-memory
11736      * data object passed to the constructor), read the data object into
11737      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11738      * process that block using the passed callback.
11739      * @param {Object} params This parameter is not used by the MemoryProxy class.
11740      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11741      * object into a block of Roo.data.Records.
11742      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11743      * The function must be passed <ul>
11744      * <li>The Record block object</li>
11745      * <li>The "arg" argument from the load function</li>
11746      * <li>A boolean success indicator</li>
11747      * </ul>
11748      * @param {Object} scope The scope in which to call the callback
11749      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11750      */
11751     load : function(params, reader, callback, scope, arg){
11752         params = params || {};
11753         var result;
11754         try {
11755             result = reader.readRecords(this.data);
11756         }catch(e){
11757             this.fireEvent("loadexception", this, arg, null, e);
11758             callback.call(scope, null, arg, false);
11759             return;
11760         }
11761         callback.call(scope, result, arg, true);
11762     },
11763     
11764     // private
11765     update : function(params, records){
11766         
11767     }
11768 });/*
11769  * Based on:
11770  * Ext JS Library 1.1.1
11771  * Copyright(c) 2006-2007, Ext JS, LLC.
11772  *
11773  * Originally Released Under LGPL - original licence link has changed is not relivant.
11774  *
11775  * Fork - LGPL
11776  * <script type="text/javascript">
11777  */
11778 /**
11779  * @class Roo.data.HttpProxy
11780  * @extends Roo.data.DataProxy
11781  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11782  * configured to reference a certain URL.<br><br>
11783  * <p>
11784  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11785  * from which the running page was served.<br><br>
11786  * <p>
11787  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11788  * <p>
11789  * Be aware that to enable the browser to parse an XML document, the server must set
11790  * the Content-Type header in the HTTP response to "text/xml".
11791  * @constructor
11792  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11793  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11794  * will be used to make the request.
11795  */
11796 Roo.data.HttpProxy = function(conn){
11797     Roo.data.HttpProxy.superclass.constructor.call(this);
11798     // is conn a conn config or a real conn?
11799     this.conn = conn;
11800     this.useAjax = !conn || !conn.events;
11801   
11802 };
11803
11804 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11805     // thse are take from connection...
11806     
11807     /**
11808      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11809      */
11810     /**
11811      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11812      * extra parameters to each request made by this object. (defaults to undefined)
11813      */
11814     /**
11815      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11816      *  to each request made by this object. (defaults to undefined)
11817      */
11818     /**
11819      * @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)
11820      */
11821     /**
11822      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11823      */
11824      /**
11825      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11826      * @type Boolean
11827      */
11828   
11829
11830     /**
11831      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11832      * @type Boolean
11833      */
11834     /**
11835      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11836      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11837      * a finer-grained basis than the DataProxy events.
11838      */
11839     getConnection : function(){
11840         return this.useAjax ? Roo.Ajax : this.conn;
11841     },
11842
11843     /**
11844      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11845      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11846      * process that block using the passed callback.
11847      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11848      * for the request to the remote server.
11849      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11850      * object into a block of Roo.data.Records.
11851      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11852      * The function must be passed <ul>
11853      * <li>The Record block object</li>
11854      * <li>The "arg" argument from the load function</li>
11855      * <li>A boolean success indicator</li>
11856      * </ul>
11857      * @param {Object} scope The scope in which to call the callback
11858      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11859      */
11860     load : function(params, reader, callback, scope, arg){
11861         if(this.fireEvent("beforeload", this, params) !== false){
11862             var  o = {
11863                 params : params || {},
11864                 request: {
11865                     callback : callback,
11866                     scope : scope,
11867                     arg : arg
11868                 },
11869                 reader: reader,
11870                 callback : this.loadResponse,
11871                 scope: this
11872             };
11873             if(this.useAjax){
11874                 Roo.applyIf(o, this.conn);
11875                 if(this.activeRequest){
11876                     Roo.Ajax.abort(this.activeRequest);
11877                 }
11878                 this.activeRequest = Roo.Ajax.request(o);
11879             }else{
11880                 this.conn.request(o);
11881             }
11882         }else{
11883             callback.call(scope||this, null, arg, false);
11884         }
11885     },
11886
11887     // private
11888     loadResponse : function(o, success, response){
11889         delete this.activeRequest;
11890         if(!success){
11891             this.fireEvent("loadexception", this, o, response);
11892             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11893             return;
11894         }
11895         var result;
11896         try {
11897             result = o.reader.read(response);
11898         }catch(e){
11899             this.fireEvent("loadexception", this, o, response, e);
11900             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11901             return;
11902         }
11903         
11904         this.fireEvent("load", this, o, o.request.arg);
11905         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11906     },
11907
11908     // private
11909     update : function(dataSet){
11910
11911     },
11912
11913     // private
11914     updateResponse : function(dataSet){
11915
11916     }
11917 });/*
11918  * Based on:
11919  * Ext JS Library 1.1.1
11920  * Copyright(c) 2006-2007, Ext JS, LLC.
11921  *
11922  * Originally Released Under LGPL - original licence link has changed is not relivant.
11923  *
11924  * Fork - LGPL
11925  * <script type="text/javascript">
11926  */
11927
11928 /**
11929  * @class Roo.data.ScriptTagProxy
11930  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11931  * other than the originating domain of the running page.<br><br>
11932  * <p>
11933  * <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
11934  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11935  * <p>
11936  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11937  * source code that is used as the source inside a &lt;script> tag.<br><br>
11938  * <p>
11939  * In order for the browser to process the returned data, the server must wrap the data object
11940  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11941  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11942  * depending on whether the callback name was passed:
11943  * <p>
11944  * <pre><code>
11945 boolean scriptTag = false;
11946 String cb = request.getParameter("callback");
11947 if (cb != null) {
11948     scriptTag = true;
11949     response.setContentType("text/javascript");
11950 } else {
11951     response.setContentType("application/x-json");
11952 }
11953 Writer out = response.getWriter();
11954 if (scriptTag) {
11955     out.write(cb + "(");
11956 }
11957 out.print(dataBlock.toJsonString());
11958 if (scriptTag) {
11959     out.write(");");
11960 }
11961 </pre></code>
11962  *
11963  * @constructor
11964  * @param {Object} config A configuration object.
11965  */
11966 Roo.data.ScriptTagProxy = function(config){
11967     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11968     Roo.apply(this, config);
11969     this.head = document.getElementsByTagName("head")[0];
11970 };
11971
11972 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11973
11974 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11975     /**
11976      * @cfg {String} url The URL from which to request the data object.
11977      */
11978     /**
11979      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11980      */
11981     timeout : 30000,
11982     /**
11983      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11984      * the server the name of the callback function set up by the load call to process the returned data object.
11985      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11986      * javascript output which calls this named function passing the data object as its only parameter.
11987      */
11988     callbackParam : "callback",
11989     /**
11990      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11991      * name to the request.
11992      */
11993     nocache : true,
11994
11995     /**
11996      * Load data from the configured URL, read the data object into
11997      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11998      * process that block using the passed callback.
11999      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12000      * for the request to the remote server.
12001      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12002      * object into a block of Roo.data.Records.
12003      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12004      * The function must be passed <ul>
12005      * <li>The Record block object</li>
12006      * <li>The "arg" argument from the load function</li>
12007      * <li>A boolean success indicator</li>
12008      * </ul>
12009      * @param {Object} scope The scope in which to call the callback
12010      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12011      */
12012     load : function(params, reader, callback, scope, arg){
12013         if(this.fireEvent("beforeload", this, params) !== false){
12014
12015             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12016
12017             var url = this.url;
12018             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12019             if(this.nocache){
12020                 url += "&_dc=" + (new Date().getTime());
12021             }
12022             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12023             var trans = {
12024                 id : transId,
12025                 cb : "stcCallback"+transId,
12026                 scriptId : "stcScript"+transId,
12027                 params : params,
12028                 arg : arg,
12029                 url : url,
12030                 callback : callback,
12031                 scope : scope,
12032                 reader : reader
12033             };
12034             var conn = this;
12035
12036             window[trans.cb] = function(o){
12037                 conn.handleResponse(o, trans);
12038             };
12039
12040             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12041
12042             if(this.autoAbort !== false){
12043                 this.abort();
12044             }
12045
12046             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12047
12048             var script = document.createElement("script");
12049             script.setAttribute("src", url);
12050             script.setAttribute("type", "text/javascript");
12051             script.setAttribute("id", trans.scriptId);
12052             this.head.appendChild(script);
12053
12054             this.trans = trans;
12055         }else{
12056             callback.call(scope||this, null, arg, false);
12057         }
12058     },
12059
12060     // private
12061     isLoading : function(){
12062         return this.trans ? true : false;
12063     },
12064
12065     /**
12066      * Abort the current server request.
12067      */
12068     abort : function(){
12069         if(this.isLoading()){
12070             this.destroyTrans(this.trans);
12071         }
12072     },
12073
12074     // private
12075     destroyTrans : function(trans, isLoaded){
12076         this.head.removeChild(document.getElementById(trans.scriptId));
12077         clearTimeout(trans.timeoutId);
12078         if(isLoaded){
12079             window[trans.cb] = undefined;
12080             try{
12081                 delete window[trans.cb];
12082             }catch(e){}
12083         }else{
12084             // if hasn't been loaded, wait for load to remove it to prevent script error
12085             window[trans.cb] = function(){
12086                 window[trans.cb] = undefined;
12087                 try{
12088                     delete window[trans.cb];
12089                 }catch(e){}
12090             };
12091         }
12092     },
12093
12094     // private
12095     handleResponse : function(o, trans){
12096         this.trans = false;
12097         this.destroyTrans(trans, true);
12098         var result;
12099         try {
12100             result = trans.reader.readRecords(o);
12101         }catch(e){
12102             this.fireEvent("loadexception", this, o, trans.arg, e);
12103             trans.callback.call(trans.scope||window, null, trans.arg, false);
12104             return;
12105         }
12106         this.fireEvent("load", this, o, trans.arg);
12107         trans.callback.call(trans.scope||window, result, trans.arg, true);
12108     },
12109
12110     // private
12111     handleFailure : function(trans){
12112         this.trans = false;
12113         this.destroyTrans(trans, false);
12114         this.fireEvent("loadexception", this, null, trans.arg);
12115         trans.callback.call(trans.scope||window, null, trans.arg, false);
12116     }
12117 });/*
12118  * Based on:
12119  * Ext JS Library 1.1.1
12120  * Copyright(c) 2006-2007, Ext JS, LLC.
12121  *
12122  * Originally Released Under LGPL - original licence link has changed is not relivant.
12123  *
12124  * Fork - LGPL
12125  * <script type="text/javascript">
12126  */
12127
12128 /**
12129  * @class Roo.data.JsonReader
12130  * @extends Roo.data.DataReader
12131  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12132  * based on mappings in a provided Roo.data.Record constructor.
12133  * 
12134  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12135  * in the reply previously. 
12136  * 
12137  * <p>
12138  * Example code:
12139  * <pre><code>
12140 var RecordDef = Roo.data.Record.create([
12141     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12142     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12143 ]);
12144 var myReader = new Roo.data.JsonReader({
12145     totalProperty: "results",    // The property which contains the total dataset size (optional)
12146     root: "rows",                // The property which contains an Array of row objects
12147     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12148 }, RecordDef);
12149 </code></pre>
12150  * <p>
12151  * This would consume a JSON file like this:
12152  * <pre><code>
12153 { 'results': 2, 'rows': [
12154     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12155     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12156 }
12157 </code></pre>
12158  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12159  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12160  * paged from the remote server.
12161  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12162  * @cfg {String} root name of the property which contains the Array of row objects.
12163  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12164  * @cfg {Array} fields Array of field definition objects
12165  * @constructor
12166  * Create a new JsonReader
12167  * @param {Object} meta Metadata configuration options
12168  * @param {Object} recordType Either an Array of field definition objects,
12169  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12170  */
12171 Roo.data.JsonReader = function(meta, recordType){
12172     
12173     meta = meta || {};
12174     // set some defaults:
12175     Roo.applyIf(meta, {
12176         totalProperty: 'total',
12177         successProperty : 'success',
12178         root : 'data',
12179         id : 'id'
12180     });
12181     
12182     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12183 };
12184 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12185     
12186     /**
12187      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12188      * Used by Store query builder to append _requestMeta to params.
12189      * 
12190      */
12191     metaFromRemote : false,
12192     /**
12193      * This method is only used by a DataProxy which has retrieved data from a remote server.
12194      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12195      * @return {Object} data A data block which is used by an Roo.data.Store object as
12196      * a cache of Roo.data.Records.
12197      */
12198     read : function(response){
12199         var json = response.responseText;
12200        
12201         var o = /* eval:var:o */ eval("("+json+")");
12202         if(!o) {
12203             throw {message: "JsonReader.read: Json object not found"};
12204         }
12205         
12206         if(o.metaData){
12207             
12208             delete this.ef;
12209             this.metaFromRemote = true;
12210             this.meta = o.metaData;
12211             this.recordType = Roo.data.Record.create(o.metaData.fields);
12212             this.onMetaChange(this.meta, this.recordType, o);
12213         }
12214         return this.readRecords(o);
12215     },
12216
12217     // private function a store will implement
12218     onMetaChange : function(meta, recordType, o){
12219
12220     },
12221
12222     /**
12223          * @ignore
12224          */
12225     simpleAccess: function(obj, subsc) {
12226         return obj[subsc];
12227     },
12228
12229         /**
12230          * @ignore
12231          */
12232     getJsonAccessor: function(){
12233         var re = /[\[\.]/;
12234         return function(expr) {
12235             try {
12236                 return(re.test(expr))
12237                     ? new Function("obj", "return obj." + expr)
12238                     : function(obj){
12239                         return obj[expr];
12240                     };
12241             } catch(e){}
12242             return Roo.emptyFn;
12243         };
12244     }(),
12245
12246     /**
12247      * Create a data block containing Roo.data.Records from an XML document.
12248      * @param {Object} o An object which contains an Array of row objects in the property specified
12249      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12250      * which contains the total size of the dataset.
12251      * @return {Object} data A data block which is used by an Roo.data.Store object as
12252      * a cache of Roo.data.Records.
12253      */
12254     readRecords : function(o){
12255         /**
12256          * After any data loads, the raw JSON data is available for further custom processing.
12257          * @type Object
12258          */
12259         this.o = o;
12260         var s = this.meta, Record = this.recordType,
12261             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12262
12263 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12264         if (!this.ef) {
12265             if(s.totalProperty) {
12266                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12267                 }
12268                 if(s.successProperty) {
12269                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12270                 }
12271                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12272                 if (s.id) {
12273                         var g = this.getJsonAccessor(s.id);
12274                         this.getId = function(rec) {
12275                                 var r = g(rec);  
12276                                 return (r === undefined || r === "") ? null : r;
12277                         };
12278                 } else {
12279                         this.getId = function(){return null;};
12280                 }
12281             this.ef = [];
12282             for(var jj = 0; jj < fl; jj++){
12283                 f = fi[jj];
12284                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12285                 this.ef[jj] = this.getJsonAccessor(map);
12286             }
12287         }
12288
12289         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12290         if(s.totalProperty){
12291             var vt = parseInt(this.getTotal(o), 10);
12292             if(!isNaN(vt)){
12293                 totalRecords = vt;
12294             }
12295         }
12296         if(s.successProperty){
12297             var vs = this.getSuccess(o);
12298             if(vs === false || vs === 'false'){
12299                 success = false;
12300             }
12301         }
12302         var records = [];
12303         for(var i = 0; i < c; i++){
12304                 var n = root[i];
12305             var values = {};
12306             var id = this.getId(n);
12307             for(var j = 0; j < fl; j++){
12308                 f = fi[j];
12309             var v = this.ef[j](n);
12310             if (!f.convert) {
12311                 Roo.log('missing convert for ' + f.name);
12312                 Roo.log(f);
12313                 continue;
12314             }
12315             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12316             }
12317             var record = new Record(values, id);
12318             record.json = n;
12319             records[i] = record;
12320         }
12321         return {
12322             raw : o,
12323             success : success,
12324             records : records,
12325             totalRecords : totalRecords
12326         };
12327     }
12328 });/*
12329  * Based on:
12330  * Ext JS Library 1.1.1
12331  * Copyright(c) 2006-2007, Ext JS, LLC.
12332  *
12333  * Originally Released Under LGPL - original licence link has changed is not relivant.
12334  *
12335  * Fork - LGPL
12336  * <script type="text/javascript">
12337  */
12338
12339 /**
12340  * @class Roo.data.ArrayReader
12341  * @extends Roo.data.DataReader
12342  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12343  * Each element of that Array represents a row of data fields. The
12344  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12345  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12346  * <p>
12347  * Example code:.
12348  * <pre><code>
12349 var RecordDef = Roo.data.Record.create([
12350     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12351     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12352 ]);
12353 var myReader = new Roo.data.ArrayReader({
12354     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12355 }, RecordDef);
12356 </code></pre>
12357  * <p>
12358  * This would consume an Array like this:
12359  * <pre><code>
12360 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12361   </code></pre>
12362  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12363  * @constructor
12364  * Create a new JsonReader
12365  * @param {Object} meta Metadata configuration options.
12366  * @param {Object} recordType Either an Array of field definition objects
12367  * as specified to {@link Roo.data.Record#create},
12368  * or an {@link Roo.data.Record} object
12369  * created using {@link Roo.data.Record#create}.
12370  */
12371 Roo.data.ArrayReader = function(meta, recordType){
12372     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12373 };
12374
12375 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12376     /**
12377      * Create a data block containing Roo.data.Records from an XML document.
12378      * @param {Object} o An Array of row objects which represents the dataset.
12379      * @return {Object} data A data block which is used by an Roo.data.Store object as
12380      * a cache of Roo.data.Records.
12381      */
12382     readRecords : function(o){
12383         var sid = this.meta ? this.meta.id : null;
12384         var recordType = this.recordType, fields = recordType.prototype.fields;
12385         var records = [];
12386         var root = o;
12387             for(var i = 0; i < root.length; i++){
12388                     var n = root[i];
12389                 var values = {};
12390                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12391                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12392                 var f = fields.items[j];
12393                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12394                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12395                 v = f.convert(v);
12396                 values[f.name] = v;
12397             }
12398                 var record = new recordType(values, id);
12399                 record.json = n;
12400                 records[records.length] = record;
12401             }
12402             return {
12403                 records : records,
12404                 totalRecords : records.length
12405             };
12406     }
12407 });/*
12408  * - LGPL
12409  * * 
12410  */
12411
12412 /**
12413  * @class Roo.bootstrap.ComboBox
12414  * @extends Roo.bootstrap.TriggerField
12415  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12416  * @cfg {Boolean} append (true|false) default false
12417  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12418  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12419  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12420  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12421  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12422  * @cfg {Boolean} animate default true
12423  * @cfg {Boolean} emptyResultText only for touch device
12424  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12425  * @cfg {String} emptyTitle default ''
12426  * @constructor
12427  * Create a new ComboBox.
12428  * @param {Object} config Configuration options
12429  */
12430 Roo.bootstrap.ComboBox = function(config){
12431     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12432     this.addEvents({
12433         /**
12434          * @event expand
12435          * Fires when the dropdown list is expanded
12436         * @param {Roo.bootstrap.ComboBox} combo This combo box
12437         */
12438         'expand' : true,
12439         /**
12440          * @event collapse
12441          * Fires when the dropdown list is collapsed
12442         * @param {Roo.bootstrap.ComboBox} combo This combo box
12443         */
12444         'collapse' : true,
12445         /**
12446          * @event beforeselect
12447          * Fires before a list item is selected. Return false to cancel the selection.
12448         * @param {Roo.bootstrap.ComboBox} combo This combo box
12449         * @param {Roo.data.Record} record The data record returned from the underlying store
12450         * @param {Number} index The index of the selected item in the dropdown list
12451         */
12452         'beforeselect' : true,
12453         /**
12454          * @event select
12455          * Fires when a list item is selected
12456         * @param {Roo.bootstrap.ComboBox} combo This combo box
12457         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12458         * @param {Number} index The index of the selected item in the dropdown list
12459         */
12460         'select' : true,
12461         /**
12462          * @event beforequery
12463          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12464          * The event object passed has these properties:
12465         * @param {Roo.bootstrap.ComboBox} combo This combo box
12466         * @param {String} query The query
12467         * @param {Boolean} forceAll true to force "all" query
12468         * @param {Boolean} cancel true to cancel the query
12469         * @param {Object} e The query event object
12470         */
12471         'beforequery': true,
12472          /**
12473          * @event add
12474          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12475         * @param {Roo.bootstrap.ComboBox} combo This combo box
12476         */
12477         'add' : true,
12478         /**
12479          * @event edit
12480          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12481         * @param {Roo.bootstrap.ComboBox} combo This combo box
12482         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12483         */
12484         'edit' : true,
12485         /**
12486          * @event remove
12487          * Fires when the remove value from the combobox array
12488         * @param {Roo.bootstrap.ComboBox} combo This combo box
12489         */
12490         'remove' : true,
12491         /**
12492          * @event afterremove
12493          * Fires when the remove value from the combobox array
12494         * @param {Roo.bootstrap.ComboBox} combo This combo box
12495         */
12496         'afterremove' : true,
12497         /**
12498          * @event specialfilter
12499          * Fires when specialfilter
12500             * @param {Roo.bootstrap.ComboBox} combo This combo box
12501             */
12502         'specialfilter' : true,
12503         /**
12504          * @event tick
12505          * Fires when tick the element
12506             * @param {Roo.bootstrap.ComboBox} combo This combo box
12507             */
12508         'tick' : true,
12509         /**
12510          * @event touchviewdisplay
12511          * Fires when touch view require special display (default is using displayField)
12512             * @param {Roo.bootstrap.ComboBox} combo This combo box
12513             * @param {Object} cfg set html .
12514             */
12515         'touchviewdisplay' : true
12516         
12517     });
12518     
12519     this.item = [];
12520     this.tickItems = [];
12521     
12522     this.selectedIndex = -1;
12523     if(this.mode == 'local'){
12524         if(config.queryDelay === undefined){
12525             this.queryDelay = 10;
12526         }
12527         if(config.minChars === undefined){
12528             this.minChars = 0;
12529         }
12530     }
12531 };
12532
12533 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12534      
12535     /**
12536      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12537      * rendering into an Roo.Editor, defaults to false)
12538      */
12539     /**
12540      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12541      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12542      */
12543     /**
12544      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12545      */
12546     /**
12547      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12548      * the dropdown list (defaults to undefined, with no header element)
12549      */
12550
12551      /**
12552      * @cfg {String/Roo.Template} tpl The template to use to render the output
12553      */
12554      
12555      /**
12556      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12557      */
12558     listWidth: undefined,
12559     /**
12560      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12561      * mode = 'remote' or 'text' if mode = 'local')
12562      */
12563     displayField: undefined,
12564     
12565     /**
12566      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12567      * mode = 'remote' or 'value' if mode = 'local'). 
12568      * Note: use of a valueField requires the user make a selection
12569      * in order for a value to be mapped.
12570      */
12571     valueField: undefined,
12572     /**
12573      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12574      */
12575     modalTitle : '',
12576     
12577     /**
12578      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12579      * field's data value (defaults to the underlying DOM element's name)
12580      */
12581     hiddenName: undefined,
12582     /**
12583      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12584      */
12585     listClass: '',
12586     /**
12587      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12588      */
12589     selectedClass: 'active',
12590     
12591     /**
12592      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12593      */
12594     shadow:'sides',
12595     /**
12596      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12597      * anchor positions (defaults to 'tl-bl')
12598      */
12599     listAlign: 'tl-bl?',
12600     /**
12601      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12602      */
12603     maxHeight: 300,
12604     /**
12605      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12606      * query specified by the allQuery config option (defaults to 'query')
12607      */
12608     triggerAction: 'query',
12609     /**
12610      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12611      * (defaults to 4, does not apply if editable = false)
12612      */
12613     minChars : 4,
12614     /**
12615      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12616      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12617      */
12618     typeAhead: false,
12619     /**
12620      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12621      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12622      */
12623     queryDelay: 500,
12624     /**
12625      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12626      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12627      */
12628     pageSize: 0,
12629     /**
12630      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12631      * when editable = true (defaults to false)
12632      */
12633     selectOnFocus:false,
12634     /**
12635      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12636      */
12637     queryParam: 'query',
12638     /**
12639      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12640      * when mode = 'remote' (defaults to 'Loading...')
12641      */
12642     loadingText: 'Loading...',
12643     /**
12644      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12645      */
12646     resizable: false,
12647     /**
12648      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12649      */
12650     handleHeight : 8,
12651     /**
12652      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12653      * traditional select (defaults to true)
12654      */
12655     editable: true,
12656     /**
12657      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12658      */
12659     allQuery: '',
12660     /**
12661      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12662      */
12663     mode: 'remote',
12664     /**
12665      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12666      * listWidth has a higher value)
12667      */
12668     minListWidth : 70,
12669     /**
12670      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12671      * allow the user to set arbitrary text into the field (defaults to false)
12672      */
12673     forceSelection:false,
12674     /**
12675      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12676      * if typeAhead = true (defaults to 250)
12677      */
12678     typeAheadDelay : 250,
12679     /**
12680      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12681      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12682      */
12683     valueNotFoundText : undefined,
12684     /**
12685      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12686      */
12687     blockFocus : false,
12688     
12689     /**
12690      * @cfg {Boolean} disableClear Disable showing of clear button.
12691      */
12692     disableClear : false,
12693     /**
12694      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12695      */
12696     alwaysQuery : false,
12697     
12698     /**
12699      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12700      */
12701     multiple : false,
12702     
12703     /**
12704      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12705      */
12706     invalidClass : "has-warning",
12707     
12708     /**
12709      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12710      */
12711     validClass : "has-success",
12712     
12713     /**
12714      * @cfg {Boolean} specialFilter (true|false) special filter default false
12715      */
12716     specialFilter : false,
12717     
12718     /**
12719      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12720      */
12721     mobileTouchView : true,
12722     
12723     /**
12724      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12725      */
12726     useNativeIOS : false,
12727     
12728     ios_options : false,
12729     
12730     //private
12731     addicon : false,
12732     editicon: false,
12733     
12734     page: 0,
12735     hasQuery: false,
12736     append: false,
12737     loadNext: false,
12738     autoFocus : true,
12739     tickable : false,
12740     btnPosition : 'right',
12741     triggerList : true,
12742     showToggleBtn : true,
12743     animate : true,
12744     emptyResultText: 'Empty',
12745     triggerText : 'Select',
12746     emptyTitle : '',
12747     
12748     // element that contains real text value.. (when hidden is used..)
12749     
12750     getAutoCreate : function()
12751     {   
12752         var cfg = false;
12753         //render
12754         /*
12755          * Render classic select for iso
12756          */
12757         
12758         if(Roo.isIOS && this.useNativeIOS){
12759             cfg = this.getAutoCreateNativeIOS();
12760             return cfg;
12761         }
12762         
12763         /*
12764          * Touch Devices
12765          */
12766         
12767         if(Roo.isTouch && this.mobileTouchView){
12768             cfg = this.getAutoCreateTouchView();
12769             return cfg;;
12770         }
12771         
12772         /*
12773          *  Normal ComboBox
12774          */
12775         if(!this.tickable){
12776             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12777             return cfg;
12778         }
12779         
12780         /*
12781          *  ComboBox with tickable selections
12782          */
12783              
12784         var align = this.labelAlign || this.parentLabelAlign();
12785         
12786         cfg = {
12787             cls : 'form-group roo-combobox-tickable' //input-group
12788         };
12789         
12790         var btn_text_select = '';
12791         var btn_text_done = '';
12792         var btn_text_cancel = '';
12793         
12794         if (this.btn_text_show) {
12795             btn_text_select = 'Select';
12796             btn_text_done = 'Done';
12797             btn_text_cancel = 'Cancel'; 
12798         }
12799         
12800         var buttons = {
12801             tag : 'div',
12802             cls : 'tickable-buttons',
12803             cn : [
12804                 {
12805                     tag : 'button',
12806                     type : 'button',
12807                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12808                     //html : this.triggerText
12809                     html: btn_text_select
12810                 },
12811                 {
12812                     tag : 'button',
12813                     type : 'button',
12814                     name : 'ok',
12815                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12816                     //html : 'Done'
12817                     html: btn_text_done
12818                 },
12819                 {
12820                     tag : 'button',
12821                     type : 'button',
12822                     name : 'cancel',
12823                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12824                     //html : 'Cancel'
12825                     html: btn_text_cancel
12826                 }
12827             ]
12828         };
12829         
12830         if(this.editable){
12831             buttons.cn.unshift({
12832                 tag: 'input',
12833                 cls: 'roo-select2-search-field-input'
12834             });
12835         }
12836         
12837         var _this = this;
12838         
12839         Roo.each(buttons.cn, function(c){
12840             if (_this.size) {
12841                 c.cls += ' btn-' + _this.size;
12842             }
12843
12844             if (_this.disabled) {
12845                 c.disabled = true;
12846             }
12847         });
12848         
12849         var box = {
12850             tag: 'div',
12851             cn: [
12852                 {
12853                     tag: 'input',
12854                     type : 'hidden',
12855                     cls: 'form-hidden-field'
12856                 },
12857                 {
12858                     tag: 'ul',
12859                     cls: 'roo-select2-choices',
12860                     cn:[
12861                         {
12862                             tag: 'li',
12863                             cls: 'roo-select2-search-field',
12864                             cn: [
12865                                 buttons
12866                             ]
12867                         }
12868                     ]
12869                 }
12870             ]
12871         };
12872         
12873         var combobox = {
12874             cls: 'roo-select2-container input-group roo-select2-container-multi',
12875             cn: [
12876                 box
12877 //                {
12878 //                    tag: 'ul',
12879 //                    cls: 'typeahead typeahead-long dropdown-menu',
12880 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12881 //                }
12882             ]
12883         };
12884         
12885         if(this.hasFeedback && !this.allowBlank){
12886             
12887             var feedback = {
12888                 tag: 'span',
12889                 cls: 'glyphicon form-control-feedback'
12890             };
12891
12892             combobox.cn.push(feedback);
12893         }
12894         
12895         
12896         if (align ==='left' && this.fieldLabel.length) {
12897             
12898             cfg.cls += ' roo-form-group-label-left';
12899             
12900             cfg.cn = [
12901                 {
12902                     tag : 'i',
12903                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12904                     tooltip : 'This field is required'
12905                 },
12906                 {
12907                     tag: 'label',
12908                     'for' :  id,
12909                     cls : 'control-label',
12910                     html : this.fieldLabel
12911
12912                 },
12913                 {
12914                     cls : "", 
12915                     cn: [
12916                         combobox
12917                     ]
12918                 }
12919
12920             ];
12921             
12922             var labelCfg = cfg.cn[1];
12923             var contentCfg = cfg.cn[2];
12924             
12925
12926             if(this.indicatorpos == 'right'){
12927                 
12928                 cfg.cn = [
12929                     {
12930                         tag: 'label',
12931                         'for' :  id,
12932                         cls : 'control-label',
12933                         cn : [
12934                             {
12935                                 tag : 'span',
12936                                 html : this.fieldLabel
12937                             },
12938                             {
12939                                 tag : 'i',
12940                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12941                                 tooltip : 'This field is required'
12942                             }
12943                         ]
12944                     },
12945                     {
12946                         cls : "",
12947                         cn: [
12948                             combobox
12949                         ]
12950                     }
12951
12952                 ];
12953                 
12954                 
12955                 
12956                 labelCfg = cfg.cn[0];
12957                 contentCfg = cfg.cn[1];
12958             
12959             }
12960             
12961             if(this.labelWidth > 12){
12962                 labelCfg.style = "width: " + this.labelWidth + 'px';
12963             }
12964             
12965             if(this.labelWidth < 13 && this.labelmd == 0){
12966                 this.labelmd = this.labelWidth;
12967             }
12968             
12969             if(this.labellg > 0){
12970                 labelCfg.cls += ' col-lg-' + this.labellg;
12971                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12972             }
12973             
12974             if(this.labelmd > 0){
12975                 labelCfg.cls += ' col-md-' + this.labelmd;
12976                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12977             }
12978             
12979             if(this.labelsm > 0){
12980                 labelCfg.cls += ' col-sm-' + this.labelsm;
12981                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12982             }
12983             
12984             if(this.labelxs > 0){
12985                 labelCfg.cls += ' col-xs-' + this.labelxs;
12986                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12987             }
12988                 
12989                 
12990         } else if ( this.fieldLabel.length) {
12991 //                Roo.log(" label");
12992                  cfg.cn = [
12993                     {
12994                         tag : 'i',
12995                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12996                         tooltip : 'This field is required'
12997                     },
12998                     {
12999                         tag: 'label',
13000                         //cls : 'input-group-addon',
13001                         html : this.fieldLabel
13002                     },
13003                     combobox
13004                 ];
13005                 
13006                 if(this.indicatorpos == 'right'){
13007                     cfg.cn = [
13008                         {
13009                             tag: 'label',
13010                             //cls : 'input-group-addon',
13011                             html : this.fieldLabel
13012                         },
13013                         {
13014                             tag : 'i',
13015                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13016                             tooltip : 'This field is required'
13017                         },
13018                         combobox
13019                     ];
13020                     
13021                 }
13022
13023         } else {
13024             
13025 //                Roo.log(" no label && no align");
13026                 cfg = combobox
13027                      
13028                 
13029         }
13030          
13031         var settings=this;
13032         ['xs','sm','md','lg'].map(function(size){
13033             if (settings[size]) {
13034                 cfg.cls += ' col-' + size + '-' + settings[size];
13035             }
13036         });
13037         
13038         return cfg;
13039         
13040     },
13041     
13042     _initEventsCalled : false,
13043     
13044     // private
13045     initEvents: function()
13046     {   
13047         if (this._initEventsCalled) { // as we call render... prevent looping...
13048             return;
13049         }
13050         this._initEventsCalled = true;
13051         
13052         if (!this.store) {
13053             throw "can not find store for combo";
13054         }
13055         
13056         this.indicator = this.indicatorEl();
13057         
13058         this.store = Roo.factory(this.store, Roo.data);
13059         this.store.parent = this;
13060         
13061         // if we are building from html. then this element is so complex, that we can not really
13062         // use the rendered HTML.
13063         // so we have to trash and replace the previous code.
13064         if (Roo.XComponent.build_from_html) {
13065             // remove this element....
13066             var e = this.el.dom, k=0;
13067             while (e ) { e = e.previousSibling;  ++k;}
13068
13069             this.el.remove();
13070             
13071             this.el=false;
13072             this.rendered = false;
13073             
13074             this.render(this.parent().getChildContainer(true), k);
13075         }
13076         
13077         if(Roo.isIOS && this.useNativeIOS){
13078             this.initIOSView();
13079             return;
13080         }
13081         
13082         /*
13083          * Touch Devices
13084          */
13085         
13086         if(Roo.isTouch && this.mobileTouchView){
13087             this.initTouchView();
13088             return;
13089         }
13090         
13091         if(this.tickable){
13092             this.initTickableEvents();
13093             return;
13094         }
13095         
13096         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13097         
13098         if(this.hiddenName){
13099             
13100             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13101             
13102             this.hiddenField.dom.value =
13103                 this.hiddenValue !== undefined ? this.hiddenValue :
13104                 this.value !== undefined ? this.value : '';
13105
13106             // prevent input submission
13107             this.el.dom.removeAttribute('name');
13108             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13109              
13110              
13111         }
13112         //if(Roo.isGecko){
13113         //    this.el.dom.setAttribute('autocomplete', 'off');
13114         //}
13115         
13116         var cls = 'x-combo-list';
13117         
13118         //this.list = new Roo.Layer({
13119         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13120         //});
13121         
13122         var _this = this;
13123         
13124         (function(){
13125             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13126             _this.list.setWidth(lw);
13127         }).defer(100);
13128         
13129         this.list.on('mouseover', this.onViewOver, this);
13130         this.list.on('mousemove', this.onViewMove, this);
13131         this.list.on('scroll', this.onViewScroll, this);
13132         
13133         /*
13134         this.list.swallowEvent('mousewheel');
13135         this.assetHeight = 0;
13136
13137         if(this.title){
13138             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13139             this.assetHeight += this.header.getHeight();
13140         }
13141
13142         this.innerList = this.list.createChild({cls:cls+'-inner'});
13143         this.innerList.on('mouseover', this.onViewOver, this);
13144         this.innerList.on('mousemove', this.onViewMove, this);
13145         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13146         
13147         if(this.allowBlank && !this.pageSize && !this.disableClear){
13148             this.footer = this.list.createChild({cls:cls+'-ft'});
13149             this.pageTb = new Roo.Toolbar(this.footer);
13150            
13151         }
13152         if(this.pageSize){
13153             this.footer = this.list.createChild({cls:cls+'-ft'});
13154             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13155                     {pageSize: this.pageSize});
13156             
13157         }
13158         
13159         if (this.pageTb && this.allowBlank && !this.disableClear) {
13160             var _this = this;
13161             this.pageTb.add(new Roo.Toolbar.Fill(), {
13162                 cls: 'x-btn-icon x-btn-clear',
13163                 text: '&#160;',
13164                 handler: function()
13165                 {
13166                     _this.collapse();
13167                     _this.clearValue();
13168                     _this.onSelect(false, -1);
13169                 }
13170             });
13171         }
13172         if (this.footer) {
13173             this.assetHeight += this.footer.getHeight();
13174         }
13175         */
13176             
13177         if(!this.tpl){
13178             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13179         }
13180
13181         this.view = new Roo.View(this.list, this.tpl, {
13182             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13183         });
13184         //this.view.wrapEl.setDisplayed(false);
13185         this.view.on('click', this.onViewClick, this);
13186         
13187         
13188         this.store.on('beforeload', this.onBeforeLoad, this);
13189         this.store.on('load', this.onLoad, this);
13190         this.store.on('loadexception', this.onLoadException, this);
13191         /*
13192         if(this.resizable){
13193             this.resizer = new Roo.Resizable(this.list,  {
13194                pinned:true, handles:'se'
13195             });
13196             this.resizer.on('resize', function(r, w, h){
13197                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13198                 this.listWidth = w;
13199                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13200                 this.restrictHeight();
13201             }, this);
13202             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13203         }
13204         */
13205         if(!this.editable){
13206             this.editable = true;
13207             this.setEditable(false);
13208         }
13209         
13210         /*
13211         
13212         if (typeof(this.events.add.listeners) != 'undefined') {
13213             
13214             this.addicon = this.wrap.createChild(
13215                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13216        
13217             this.addicon.on('click', function(e) {
13218                 this.fireEvent('add', this);
13219             }, this);
13220         }
13221         if (typeof(this.events.edit.listeners) != 'undefined') {
13222             
13223             this.editicon = this.wrap.createChild(
13224                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13225             if (this.addicon) {
13226                 this.editicon.setStyle('margin-left', '40px');
13227             }
13228             this.editicon.on('click', function(e) {
13229                 
13230                 // we fire even  if inothing is selected..
13231                 this.fireEvent('edit', this, this.lastData );
13232                 
13233             }, this);
13234         }
13235         */
13236         
13237         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13238             "up" : function(e){
13239                 this.inKeyMode = true;
13240                 this.selectPrev();
13241             },
13242
13243             "down" : function(e){
13244                 if(!this.isExpanded()){
13245                     this.onTriggerClick();
13246                 }else{
13247                     this.inKeyMode = true;
13248                     this.selectNext();
13249                 }
13250             },
13251
13252             "enter" : function(e){
13253 //                this.onViewClick();
13254                 //return true;
13255                 this.collapse();
13256                 
13257                 if(this.fireEvent("specialkey", this, e)){
13258                     this.onViewClick(false);
13259                 }
13260                 
13261                 return true;
13262             },
13263
13264             "esc" : function(e){
13265                 this.collapse();
13266             },
13267
13268             "tab" : function(e){
13269                 this.collapse();
13270                 
13271                 if(this.fireEvent("specialkey", this, e)){
13272                     this.onViewClick(false);
13273                 }
13274                 
13275                 return true;
13276             },
13277
13278             scope : this,
13279
13280             doRelay : function(foo, bar, hname){
13281                 if(hname == 'down' || this.scope.isExpanded()){
13282                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13283                 }
13284                 return true;
13285             },
13286
13287             forceKeyDown: true
13288         });
13289         
13290         
13291         this.queryDelay = Math.max(this.queryDelay || 10,
13292                 this.mode == 'local' ? 10 : 250);
13293         
13294         
13295         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13296         
13297         if(this.typeAhead){
13298             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13299         }
13300         if(this.editable !== false){
13301             this.inputEl().on("keyup", this.onKeyUp, this);
13302         }
13303         if(this.forceSelection){
13304             this.inputEl().on('blur', this.doForce, this);
13305         }
13306         
13307         if(this.multiple){
13308             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13309             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13310         }
13311     },
13312     
13313     initTickableEvents: function()
13314     {   
13315         this.createList();
13316         
13317         if(this.hiddenName){
13318             
13319             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13320             
13321             this.hiddenField.dom.value =
13322                 this.hiddenValue !== undefined ? this.hiddenValue :
13323                 this.value !== undefined ? this.value : '';
13324
13325             // prevent input submission
13326             this.el.dom.removeAttribute('name');
13327             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13328              
13329              
13330         }
13331         
13332 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13333         
13334         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13335         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13336         if(this.triggerList){
13337             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13338         }
13339          
13340         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13341         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13342         
13343         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13344         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13345         
13346         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13347         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13348         
13349         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13350         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13351         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13352         
13353         this.okBtn.hide();
13354         this.cancelBtn.hide();
13355         
13356         var _this = this;
13357         
13358         (function(){
13359             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13360             _this.list.setWidth(lw);
13361         }).defer(100);
13362         
13363         this.list.on('mouseover', this.onViewOver, this);
13364         this.list.on('mousemove', this.onViewMove, this);
13365         
13366         this.list.on('scroll', this.onViewScroll, this);
13367         
13368         if(!this.tpl){
13369             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>';
13370         }
13371
13372         this.view = new Roo.View(this.list, this.tpl, {
13373             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13374         });
13375         
13376         //this.view.wrapEl.setDisplayed(false);
13377         this.view.on('click', this.onViewClick, this);
13378         
13379         
13380         
13381         this.store.on('beforeload', this.onBeforeLoad, this);
13382         this.store.on('load', this.onLoad, this);
13383         this.store.on('loadexception', this.onLoadException, this);
13384         
13385         if(this.editable){
13386             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13387                 "up" : function(e){
13388                     this.inKeyMode = true;
13389                     this.selectPrev();
13390                 },
13391
13392                 "down" : function(e){
13393                     this.inKeyMode = true;
13394                     this.selectNext();
13395                 },
13396
13397                 "enter" : function(e){
13398                     if(this.fireEvent("specialkey", this, e)){
13399                         this.onViewClick(false);
13400                     }
13401                     
13402                     return true;
13403                 },
13404
13405                 "esc" : function(e){
13406                     this.onTickableFooterButtonClick(e, false, false);
13407                 },
13408
13409                 "tab" : function(e){
13410                     this.fireEvent("specialkey", this, e);
13411                     
13412                     this.onTickableFooterButtonClick(e, false, false);
13413                     
13414                     return true;
13415                 },
13416
13417                 scope : this,
13418
13419                 doRelay : function(e, fn, key){
13420                     if(this.scope.isExpanded()){
13421                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13422                     }
13423                     return true;
13424                 },
13425
13426                 forceKeyDown: true
13427             });
13428         }
13429         
13430         this.queryDelay = Math.max(this.queryDelay || 10,
13431                 this.mode == 'local' ? 10 : 250);
13432         
13433         
13434         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13435         
13436         if(this.typeAhead){
13437             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13438         }
13439         
13440         if(this.editable !== false){
13441             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13442         }
13443         
13444         this.indicator = this.indicatorEl();
13445         
13446         if(this.indicator){
13447             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13448             this.indicator.hide();
13449         }
13450         
13451     },
13452
13453     onDestroy : function(){
13454         if(this.view){
13455             this.view.setStore(null);
13456             this.view.el.removeAllListeners();
13457             this.view.el.remove();
13458             this.view.purgeListeners();
13459         }
13460         if(this.list){
13461             this.list.dom.innerHTML  = '';
13462         }
13463         
13464         if(this.store){
13465             this.store.un('beforeload', this.onBeforeLoad, this);
13466             this.store.un('load', this.onLoad, this);
13467             this.store.un('loadexception', this.onLoadException, this);
13468         }
13469         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13470     },
13471
13472     // private
13473     fireKey : function(e){
13474         if(e.isNavKeyPress() && !this.list.isVisible()){
13475             this.fireEvent("specialkey", this, e);
13476         }
13477     },
13478
13479     // private
13480     onResize: function(w, h){
13481 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13482 //        
13483 //        if(typeof w != 'number'){
13484 //            // we do not handle it!?!?
13485 //            return;
13486 //        }
13487 //        var tw = this.trigger.getWidth();
13488 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13489 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13490 //        var x = w - tw;
13491 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13492 //            
13493 //        //this.trigger.setStyle('left', x+'px');
13494 //        
13495 //        if(this.list && this.listWidth === undefined){
13496 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13497 //            this.list.setWidth(lw);
13498 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13499 //        }
13500         
13501     
13502         
13503     },
13504
13505     /**
13506      * Allow or prevent the user from directly editing the field text.  If false is passed,
13507      * the user will only be able to select from the items defined in the dropdown list.  This method
13508      * is the runtime equivalent of setting the 'editable' config option at config time.
13509      * @param {Boolean} value True to allow the user to directly edit the field text
13510      */
13511     setEditable : function(value){
13512         if(value == this.editable){
13513             return;
13514         }
13515         this.editable = value;
13516         if(!value){
13517             this.inputEl().dom.setAttribute('readOnly', true);
13518             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13519             this.inputEl().addClass('x-combo-noedit');
13520         }else{
13521             this.inputEl().dom.setAttribute('readOnly', false);
13522             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13523             this.inputEl().removeClass('x-combo-noedit');
13524         }
13525     },
13526
13527     // private
13528     
13529     onBeforeLoad : function(combo,opts){
13530         if(!this.hasFocus){
13531             return;
13532         }
13533          if (!opts.add) {
13534             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13535          }
13536         this.restrictHeight();
13537         this.selectedIndex = -1;
13538     },
13539
13540     // private
13541     onLoad : function(){
13542         
13543         this.hasQuery = false;
13544         
13545         if(!this.hasFocus){
13546             return;
13547         }
13548         
13549         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13550             this.loading.hide();
13551         }
13552         
13553         if(this.store.getCount() > 0){
13554             
13555             this.expand();
13556             this.restrictHeight();
13557             if(this.lastQuery == this.allQuery){
13558                 if(this.editable && !this.tickable){
13559                     this.inputEl().dom.select();
13560                 }
13561                 
13562                 if(
13563                     !this.selectByValue(this.value, true) &&
13564                     this.autoFocus && 
13565                     (
13566                         !this.store.lastOptions ||
13567                         typeof(this.store.lastOptions.add) == 'undefined' || 
13568                         this.store.lastOptions.add != true
13569                     )
13570                 ){
13571                     this.select(0, true);
13572                 }
13573             }else{
13574                 if(this.autoFocus){
13575                     this.selectNext();
13576                 }
13577                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13578                     this.taTask.delay(this.typeAheadDelay);
13579                 }
13580             }
13581         }else{
13582             this.onEmptyResults();
13583         }
13584         
13585         //this.el.focus();
13586     },
13587     // private
13588     onLoadException : function()
13589     {
13590         this.hasQuery = false;
13591         
13592         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13593             this.loading.hide();
13594         }
13595         
13596         if(this.tickable && this.editable){
13597             return;
13598         }
13599         
13600         this.collapse();
13601         // only causes errors at present
13602         //Roo.log(this.store.reader.jsonData);
13603         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13604             // fixme
13605             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13606         //}
13607         
13608         
13609     },
13610     // private
13611     onTypeAhead : function(){
13612         if(this.store.getCount() > 0){
13613             var r = this.store.getAt(0);
13614             var newValue = r.data[this.displayField];
13615             var len = newValue.length;
13616             var selStart = this.getRawValue().length;
13617             
13618             if(selStart != len){
13619                 this.setRawValue(newValue);
13620                 this.selectText(selStart, newValue.length);
13621             }
13622         }
13623     },
13624
13625     // private
13626     onSelect : function(record, index){
13627         
13628         if(this.fireEvent('beforeselect', this, record, index) !== false){
13629         
13630             this.setFromData(index > -1 ? record.data : false);
13631             
13632             this.collapse();
13633             this.fireEvent('select', this, record, index);
13634         }
13635     },
13636
13637     /**
13638      * Returns the currently selected field value or empty string if no value is set.
13639      * @return {String} value The selected value
13640      */
13641     getValue : function()
13642     {
13643         if(Roo.isIOS && this.useNativeIOS){
13644             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13645         }
13646         
13647         if(this.multiple){
13648             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13649         }
13650         
13651         if(this.valueField){
13652             return typeof this.value != 'undefined' ? this.value : '';
13653         }else{
13654             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13655         }
13656     },
13657     
13658     getRawValue : function()
13659     {
13660         if(Roo.isIOS && this.useNativeIOS){
13661             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13662         }
13663         
13664         var v = this.inputEl().getValue();
13665         
13666         return v;
13667     },
13668
13669     /**
13670      * Clears any text/value currently set in the field
13671      */
13672     clearValue : function(){
13673         
13674         if(this.hiddenField){
13675             this.hiddenField.dom.value = '';
13676         }
13677         this.value = '';
13678         this.setRawValue('');
13679         this.lastSelectionText = '';
13680         this.lastData = false;
13681         
13682         var close = this.closeTriggerEl();
13683         
13684         if(close){
13685             close.hide();
13686         }
13687         
13688         this.validate();
13689         
13690     },
13691
13692     /**
13693      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13694      * will be displayed in the field.  If the value does not match the data value of an existing item,
13695      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13696      * Otherwise the field will be blank (although the value will still be set).
13697      * @param {String} value The value to match
13698      */
13699     setValue : function(v)
13700     {
13701         if(Roo.isIOS && this.useNativeIOS){
13702             this.setIOSValue(v);
13703             return;
13704         }
13705         
13706         if(this.multiple){
13707             this.syncValue();
13708             return;
13709         }
13710         
13711         var text = v;
13712         if(this.valueField){
13713             var r = this.findRecord(this.valueField, v);
13714             if(r){
13715                 text = r.data[this.displayField];
13716             }else if(this.valueNotFoundText !== undefined){
13717                 text = this.valueNotFoundText;
13718             }
13719         }
13720         this.lastSelectionText = text;
13721         if(this.hiddenField){
13722             this.hiddenField.dom.value = v;
13723         }
13724         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13725         this.value = v;
13726         
13727         var close = this.closeTriggerEl();
13728         
13729         if(close){
13730             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13731         }
13732         
13733         this.validate();
13734     },
13735     /**
13736      * @property {Object} the last set data for the element
13737      */
13738     
13739     lastData : false,
13740     /**
13741      * Sets the value of the field based on a object which is related to the record format for the store.
13742      * @param {Object} value the value to set as. or false on reset?
13743      */
13744     setFromData : function(o){
13745         
13746         if(this.multiple){
13747             this.addItem(o);
13748             return;
13749         }
13750             
13751         var dv = ''; // display value
13752         var vv = ''; // value value..
13753         this.lastData = o;
13754         if (this.displayField) {
13755             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13756         } else {
13757             // this is an error condition!!!
13758             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13759         }
13760         
13761         if(this.valueField){
13762             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13763         }
13764         
13765         var close = this.closeTriggerEl();
13766         
13767         if(close){
13768             if(dv.length || vv * 1 > 0){
13769                 close.show() ;
13770                 this.blockFocus=true;
13771             } else {
13772                 close.hide();
13773             }             
13774         }
13775         
13776         if(this.hiddenField){
13777             this.hiddenField.dom.value = vv;
13778             
13779             this.lastSelectionText = dv;
13780             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13781             this.value = vv;
13782             return;
13783         }
13784         // no hidden field.. - we store the value in 'value', but still display
13785         // display field!!!!
13786         this.lastSelectionText = dv;
13787         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13788         this.value = vv;
13789         
13790         
13791         
13792     },
13793     // private
13794     reset : function(){
13795         // overridden so that last data is reset..
13796         
13797         if(this.multiple){
13798             this.clearItem();
13799             return;
13800         }
13801         
13802         this.setValue(this.originalValue);
13803         //this.clearInvalid();
13804         this.lastData = false;
13805         if (this.view) {
13806             this.view.clearSelections();
13807         }
13808         
13809         this.validate();
13810     },
13811     // private
13812     findRecord : function(prop, value){
13813         var record;
13814         if(this.store.getCount() > 0){
13815             this.store.each(function(r){
13816                 if(r.data[prop] == value){
13817                     record = r;
13818                     return false;
13819                 }
13820                 return true;
13821             });
13822         }
13823         return record;
13824     },
13825     
13826     getName: function()
13827     {
13828         // returns hidden if it's set..
13829         if (!this.rendered) {return ''};
13830         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13831         
13832     },
13833     // private
13834     onViewMove : function(e, t){
13835         this.inKeyMode = false;
13836     },
13837
13838     // private
13839     onViewOver : function(e, t){
13840         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13841             return;
13842         }
13843         var item = this.view.findItemFromChild(t);
13844         
13845         if(item){
13846             var index = this.view.indexOf(item);
13847             this.select(index, false);
13848         }
13849     },
13850
13851     // private
13852     onViewClick : function(view, doFocus, el, e)
13853     {
13854         var index = this.view.getSelectedIndexes()[0];
13855         
13856         var r = this.store.getAt(index);
13857         
13858         if(this.tickable){
13859             
13860             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13861                 return;
13862             }
13863             
13864             var rm = false;
13865             var _this = this;
13866             
13867             Roo.each(this.tickItems, function(v,k){
13868                 
13869                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13870                     Roo.log(v);
13871                     _this.tickItems.splice(k, 1);
13872                     
13873                     if(typeof(e) == 'undefined' && view == false){
13874                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13875                     }
13876                     
13877                     rm = true;
13878                     return;
13879                 }
13880             });
13881             
13882             if(rm){
13883                 return;
13884             }
13885             
13886             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13887                 this.tickItems.push(r.data);
13888             }
13889             
13890             if(typeof(e) == 'undefined' && view == false){
13891                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13892             }
13893                     
13894             return;
13895         }
13896         
13897         if(r){
13898             this.onSelect(r, index);
13899         }
13900         if(doFocus !== false && !this.blockFocus){
13901             this.inputEl().focus();
13902         }
13903     },
13904
13905     // private
13906     restrictHeight : function(){
13907         //this.innerList.dom.style.height = '';
13908         //var inner = this.innerList.dom;
13909         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13910         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13911         //this.list.beginUpdate();
13912         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13913         this.list.alignTo(this.inputEl(), this.listAlign);
13914         this.list.alignTo(this.inputEl(), this.listAlign);
13915         //this.list.endUpdate();
13916     },
13917
13918     // private
13919     onEmptyResults : function(){
13920         
13921         if(this.tickable && this.editable){
13922             this.restrictHeight();
13923             return;
13924         }
13925         
13926         this.collapse();
13927     },
13928
13929     /**
13930      * Returns true if the dropdown list is expanded, else false.
13931      */
13932     isExpanded : function(){
13933         return this.list.isVisible();
13934     },
13935
13936     /**
13937      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13938      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13939      * @param {String} value The data value of the item to select
13940      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13941      * selected item if it is not currently in view (defaults to true)
13942      * @return {Boolean} True if the value matched an item in the list, else false
13943      */
13944     selectByValue : function(v, scrollIntoView){
13945         if(v !== undefined && v !== null){
13946             var r = this.findRecord(this.valueField || this.displayField, v);
13947             if(r){
13948                 this.select(this.store.indexOf(r), scrollIntoView);
13949                 return true;
13950             }
13951         }
13952         return false;
13953     },
13954
13955     /**
13956      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13957      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13958      * @param {Number} index The zero-based index of the list item to select
13959      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13960      * selected item if it is not currently in view (defaults to true)
13961      */
13962     select : function(index, scrollIntoView){
13963         this.selectedIndex = index;
13964         this.view.select(index);
13965         if(scrollIntoView !== false){
13966             var el = this.view.getNode(index);
13967             /*
13968              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13969              */
13970             if(el){
13971                 this.list.scrollChildIntoView(el, false);
13972             }
13973         }
13974     },
13975
13976     // private
13977     selectNext : function(){
13978         var ct = this.store.getCount();
13979         if(ct > 0){
13980             if(this.selectedIndex == -1){
13981                 this.select(0);
13982             }else if(this.selectedIndex < ct-1){
13983                 this.select(this.selectedIndex+1);
13984             }
13985         }
13986     },
13987
13988     // private
13989     selectPrev : function(){
13990         var ct = this.store.getCount();
13991         if(ct > 0){
13992             if(this.selectedIndex == -1){
13993                 this.select(0);
13994             }else if(this.selectedIndex != 0){
13995                 this.select(this.selectedIndex-1);
13996             }
13997         }
13998     },
13999
14000     // private
14001     onKeyUp : function(e){
14002         if(this.editable !== false && !e.isSpecialKey()){
14003             this.lastKey = e.getKey();
14004             this.dqTask.delay(this.queryDelay);
14005         }
14006     },
14007
14008     // private
14009     validateBlur : function(){
14010         return !this.list || !this.list.isVisible();   
14011     },
14012
14013     // private
14014     initQuery : function(){
14015         
14016         var v = this.getRawValue();
14017         
14018         if(this.tickable && this.editable){
14019             v = this.tickableInputEl().getValue();
14020         }
14021         
14022         this.doQuery(v);
14023     },
14024
14025     // private
14026     doForce : function(){
14027         if(this.inputEl().dom.value.length > 0){
14028             this.inputEl().dom.value =
14029                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14030              
14031         }
14032     },
14033
14034     /**
14035      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14036      * query allowing the query action to be canceled if needed.
14037      * @param {String} query The SQL query to execute
14038      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14039      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14040      * saved in the current store (defaults to false)
14041      */
14042     doQuery : function(q, forceAll){
14043         
14044         if(q === undefined || q === null){
14045             q = '';
14046         }
14047         var qe = {
14048             query: q,
14049             forceAll: forceAll,
14050             combo: this,
14051             cancel:false
14052         };
14053         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14054             return false;
14055         }
14056         
14057         Roo.log(qe);
14058         
14059         q = qe.query;
14060         
14061         forceAll = qe.forceAll;
14062         if(forceAll === true || (q.length >= this.minChars)){
14063             
14064             this.hasQuery = true;
14065             Roo.log('in??');
14066             if(this.lastQuery != q || this.alwaysQuery){
14067                 this.lastQuery = q;
14068                 if(this.mode == 'local'){
14069                     this.selectedIndex = -1;
14070                     if(forceAll){
14071                         this.store.clearFilter();
14072                     }else{
14073                         
14074                         if(this.specialFilter){
14075                             this.fireEvent('specialfilter', this);
14076                             this.onLoad();
14077                             return;
14078                         }
14079                         
14080                         this.store.filter(this.displayField, q);
14081                     }
14082                     
14083                     this.store.fireEvent("datachanged", this.store);
14084                     
14085                     this.onLoad();
14086                     
14087                     Roo.log('onload???');
14088                     
14089                 }else{
14090                     
14091                     this.store.baseParams[this.queryParam] = q;
14092                     
14093                     var options = {params : this.getParams(q)};
14094                     
14095                     if(this.loadNext){
14096                         options.add = true;
14097                         options.params.start = this.page * this.pageSize;
14098                     }
14099                     
14100                     this.store.load(options);
14101                     
14102                     /*
14103                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14104                      *  we should expand the list on onLoad
14105                      *  so command out it
14106                      */
14107 //                    this.expand();
14108                 }
14109             }else{
14110                 Roo.log('in!!??');
14111                 this.selectedIndex = -1;
14112                 this.onLoad();   
14113             }
14114         }
14115         
14116         this.loadNext = false;
14117     },
14118     
14119     // private
14120     getParams : function(q){
14121         var p = {};
14122         //p[this.queryParam] = q;
14123         
14124         if(this.pageSize){
14125             p.start = 0;
14126             p.limit = this.pageSize;
14127         }
14128         return p;
14129     },
14130
14131     /**
14132      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14133      */
14134     collapse : function(){
14135         if(!this.isExpanded()){
14136             return;
14137         }
14138         
14139         this.list.hide();
14140         
14141         this.hasFocus = false;
14142         
14143         if(this.tickable){
14144             this.okBtn.hide();
14145             this.cancelBtn.hide();
14146             this.trigger.show();
14147             
14148             if(this.editable){
14149                 this.tickableInputEl().dom.value = '';
14150                 this.tickableInputEl().blur();
14151             }
14152             
14153         }
14154         
14155         Roo.get(document).un('mousedown', this.collapseIf, this);
14156         Roo.get(document).un('mousewheel', this.collapseIf, this);
14157         if (!this.editable) {
14158             Roo.get(document).un('keydown', this.listKeyPress, this);
14159         }
14160         this.fireEvent('collapse', this);
14161         
14162         this.validate();
14163     },
14164
14165     // private
14166     collapseIf : function(e){
14167         var in_combo  = e.within(this.el);
14168         var in_list =  e.within(this.list);
14169         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14170         
14171         if (in_combo || in_list || is_list) {
14172             //e.stopPropagation();
14173             return;
14174         }
14175         
14176         if(this.tickable){
14177             this.onTickableFooterButtonClick(e, false, false);
14178         }
14179
14180         this.collapse();
14181         
14182     },
14183
14184     /**
14185      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14186      */
14187     expand : function(){
14188        
14189         if(this.isExpanded() || !this.hasFocus){
14190             return;
14191         }
14192         
14193         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14194         this.list.setWidth(lw);
14195         
14196         Roo.log('expand');
14197         
14198         this.list.show();
14199         
14200         this.restrictHeight();
14201         
14202         if(this.tickable){
14203             
14204             this.tickItems = Roo.apply([], this.item);
14205             
14206             this.okBtn.show();
14207             this.cancelBtn.show();
14208             this.trigger.hide();
14209             
14210             if(this.editable){
14211                 this.tickableInputEl().focus();
14212             }
14213             
14214         }
14215         
14216         Roo.get(document).on('mousedown', this.collapseIf, this);
14217         Roo.get(document).on('mousewheel', this.collapseIf, this);
14218         if (!this.editable) {
14219             Roo.get(document).on('keydown', this.listKeyPress, this);
14220         }
14221         
14222         this.fireEvent('expand', this);
14223     },
14224
14225     // private
14226     // Implements the default empty TriggerField.onTriggerClick function
14227     onTriggerClick : function(e)
14228     {
14229         Roo.log('trigger click');
14230         
14231         if(this.disabled || !this.triggerList){
14232             return;
14233         }
14234         
14235         this.page = 0;
14236         this.loadNext = false;
14237         
14238         if(this.isExpanded()){
14239             this.collapse();
14240             if (!this.blockFocus) {
14241                 this.inputEl().focus();
14242             }
14243             
14244         }else {
14245             this.hasFocus = true;
14246             if(this.triggerAction == 'all') {
14247                 this.doQuery(this.allQuery, true);
14248             } else {
14249                 this.doQuery(this.getRawValue());
14250             }
14251             if (!this.blockFocus) {
14252                 this.inputEl().focus();
14253             }
14254         }
14255     },
14256     
14257     onTickableTriggerClick : function(e)
14258     {
14259         if(this.disabled){
14260             return;
14261         }
14262         
14263         this.page = 0;
14264         this.loadNext = false;
14265         this.hasFocus = true;
14266         
14267         if(this.triggerAction == 'all') {
14268             this.doQuery(this.allQuery, true);
14269         } else {
14270             this.doQuery(this.getRawValue());
14271         }
14272     },
14273     
14274     onSearchFieldClick : function(e)
14275     {
14276         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14277             this.onTickableFooterButtonClick(e, false, false);
14278             return;
14279         }
14280         
14281         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14282             return;
14283         }
14284         
14285         this.page = 0;
14286         this.loadNext = false;
14287         this.hasFocus = true;
14288         
14289         if(this.triggerAction == 'all') {
14290             this.doQuery(this.allQuery, true);
14291         } else {
14292             this.doQuery(this.getRawValue());
14293         }
14294     },
14295     
14296     listKeyPress : function(e)
14297     {
14298         //Roo.log('listkeypress');
14299         // scroll to first matching element based on key pres..
14300         if (e.isSpecialKey()) {
14301             return false;
14302         }
14303         var k = String.fromCharCode(e.getKey()).toUpperCase();
14304         //Roo.log(k);
14305         var match  = false;
14306         var csel = this.view.getSelectedNodes();
14307         var cselitem = false;
14308         if (csel.length) {
14309             var ix = this.view.indexOf(csel[0]);
14310             cselitem  = this.store.getAt(ix);
14311             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14312                 cselitem = false;
14313             }
14314             
14315         }
14316         
14317         this.store.each(function(v) { 
14318             if (cselitem) {
14319                 // start at existing selection.
14320                 if (cselitem.id == v.id) {
14321                     cselitem = false;
14322                 }
14323                 return true;
14324             }
14325                 
14326             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14327                 match = this.store.indexOf(v);
14328                 return false;
14329             }
14330             return true;
14331         }, this);
14332         
14333         if (match === false) {
14334             return true; // no more action?
14335         }
14336         // scroll to?
14337         this.view.select(match);
14338         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14339         sn.scrollIntoView(sn.dom.parentNode, false);
14340     },
14341     
14342     onViewScroll : function(e, t){
14343         
14344         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){
14345             return;
14346         }
14347         
14348         this.hasQuery = true;
14349         
14350         this.loading = this.list.select('.loading', true).first();
14351         
14352         if(this.loading === null){
14353             this.list.createChild({
14354                 tag: 'div',
14355                 cls: 'loading roo-select2-more-results roo-select2-active',
14356                 html: 'Loading more results...'
14357             });
14358             
14359             this.loading = this.list.select('.loading', true).first();
14360             
14361             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14362             
14363             this.loading.hide();
14364         }
14365         
14366         this.loading.show();
14367         
14368         var _combo = this;
14369         
14370         this.page++;
14371         this.loadNext = true;
14372         
14373         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14374         
14375         return;
14376     },
14377     
14378     addItem : function(o)
14379     {   
14380         var dv = ''; // display value
14381         
14382         if (this.displayField) {
14383             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14384         } else {
14385             // this is an error condition!!!
14386             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14387         }
14388         
14389         if(!dv.length){
14390             return;
14391         }
14392         
14393         var choice = this.choices.createChild({
14394             tag: 'li',
14395             cls: 'roo-select2-search-choice',
14396             cn: [
14397                 {
14398                     tag: 'div',
14399                     html: dv
14400                 },
14401                 {
14402                     tag: 'a',
14403                     href: '#',
14404                     cls: 'roo-select2-search-choice-close fa fa-times',
14405                     tabindex: '-1'
14406                 }
14407             ]
14408             
14409         }, this.searchField);
14410         
14411         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14412         
14413         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14414         
14415         this.item.push(o);
14416         
14417         this.lastData = o;
14418         
14419         this.syncValue();
14420         
14421         this.inputEl().dom.value = '';
14422         
14423         this.validate();
14424     },
14425     
14426     onRemoveItem : function(e, _self, o)
14427     {
14428         e.preventDefault();
14429         
14430         this.lastItem = Roo.apply([], this.item);
14431         
14432         var index = this.item.indexOf(o.data) * 1;
14433         
14434         if( index < 0){
14435             Roo.log('not this item?!');
14436             return;
14437         }
14438         
14439         this.item.splice(index, 1);
14440         o.item.remove();
14441         
14442         this.syncValue();
14443         
14444         this.fireEvent('remove', this, e);
14445         
14446         this.validate();
14447         
14448     },
14449     
14450     syncValue : function()
14451     {
14452         if(!this.item.length){
14453             this.clearValue();
14454             return;
14455         }
14456             
14457         var value = [];
14458         var _this = this;
14459         Roo.each(this.item, function(i){
14460             if(_this.valueField){
14461                 value.push(i[_this.valueField]);
14462                 return;
14463             }
14464
14465             value.push(i);
14466         });
14467
14468         this.value = value.join(',');
14469
14470         if(this.hiddenField){
14471             this.hiddenField.dom.value = this.value;
14472         }
14473         
14474         this.store.fireEvent("datachanged", this.store);
14475         
14476         this.validate();
14477     },
14478     
14479     clearItem : function()
14480     {
14481         if(!this.multiple){
14482             return;
14483         }
14484         
14485         this.item = [];
14486         
14487         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14488            c.remove();
14489         });
14490         
14491         this.syncValue();
14492         
14493         this.validate();
14494         
14495         if(this.tickable && !Roo.isTouch){
14496             this.view.refresh();
14497         }
14498     },
14499     
14500     inputEl: function ()
14501     {
14502         if(Roo.isIOS && this.useNativeIOS){
14503             return this.el.select('select.roo-ios-select', true).first();
14504         }
14505         
14506         if(Roo.isTouch && this.mobileTouchView){
14507             return this.el.select('input.form-control',true).first();
14508         }
14509         
14510         if(this.tickable){
14511             return this.searchField;
14512         }
14513         
14514         return this.el.select('input.form-control',true).first();
14515     },
14516     
14517     onTickableFooterButtonClick : function(e, btn, el)
14518     {
14519         e.preventDefault();
14520         
14521         this.lastItem = Roo.apply([], this.item);
14522         
14523         if(btn && btn.name == 'cancel'){
14524             this.tickItems = Roo.apply([], this.item);
14525             this.collapse();
14526             return;
14527         }
14528         
14529         this.clearItem();
14530         
14531         var _this = this;
14532         
14533         Roo.each(this.tickItems, function(o){
14534             _this.addItem(o);
14535         });
14536         
14537         this.collapse();
14538         
14539     },
14540     
14541     validate : function()
14542     {
14543         var v = this.getRawValue();
14544         
14545         if(this.multiple){
14546             v = this.getValue();
14547         }
14548         
14549         if(this.disabled || this.allowBlank || v.length){
14550             this.markValid();
14551             return true;
14552         }
14553         
14554         this.markInvalid();
14555         return false;
14556     },
14557     
14558     tickableInputEl : function()
14559     {
14560         if(!this.tickable || !this.editable){
14561             return this.inputEl();
14562         }
14563         
14564         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14565     },
14566     
14567     
14568     getAutoCreateTouchView : function()
14569     {
14570         var id = Roo.id();
14571         
14572         var cfg = {
14573             cls: 'form-group' //input-group
14574         };
14575         
14576         var input =  {
14577             tag: 'input',
14578             id : id,
14579             type : this.inputType,
14580             cls : 'form-control x-combo-noedit',
14581             autocomplete: 'new-password',
14582             placeholder : this.placeholder || '',
14583             readonly : true
14584         };
14585         
14586         if (this.name) {
14587             input.name = this.name;
14588         }
14589         
14590         if (this.size) {
14591             input.cls += ' input-' + this.size;
14592         }
14593         
14594         if (this.disabled) {
14595             input.disabled = true;
14596         }
14597         
14598         var inputblock = {
14599             cls : '',
14600             cn : [
14601                 input
14602             ]
14603         };
14604         
14605         if(this.before){
14606             inputblock.cls += ' input-group';
14607             
14608             inputblock.cn.unshift({
14609                 tag :'span',
14610                 cls : 'input-group-addon',
14611                 html : this.before
14612             });
14613         }
14614         
14615         if(this.removable && !this.multiple){
14616             inputblock.cls += ' roo-removable';
14617             
14618             inputblock.cn.push({
14619                 tag: 'button',
14620                 html : 'x',
14621                 cls : 'roo-combo-removable-btn close'
14622             });
14623         }
14624
14625         if(this.hasFeedback && !this.allowBlank){
14626             
14627             inputblock.cls += ' has-feedback';
14628             
14629             inputblock.cn.push({
14630                 tag: 'span',
14631                 cls: 'glyphicon form-control-feedback'
14632             });
14633             
14634         }
14635         
14636         if (this.after) {
14637             
14638             inputblock.cls += (this.before) ? '' : ' input-group';
14639             
14640             inputblock.cn.push({
14641                 tag :'span',
14642                 cls : 'input-group-addon',
14643                 html : this.after
14644             });
14645         }
14646
14647         var box = {
14648             tag: 'div',
14649             cn: [
14650                 {
14651                     tag: 'input',
14652                     type : 'hidden',
14653                     cls: 'form-hidden-field'
14654                 },
14655                 inputblock
14656             ]
14657             
14658         };
14659         
14660         if(this.multiple){
14661             box = {
14662                 tag: 'div',
14663                 cn: [
14664                     {
14665                         tag: 'input',
14666                         type : 'hidden',
14667                         cls: 'form-hidden-field'
14668                     },
14669                     {
14670                         tag: 'ul',
14671                         cls: 'roo-select2-choices',
14672                         cn:[
14673                             {
14674                                 tag: 'li',
14675                                 cls: 'roo-select2-search-field',
14676                                 cn: [
14677
14678                                     inputblock
14679                                 ]
14680                             }
14681                         ]
14682                     }
14683                 ]
14684             }
14685         };
14686         
14687         var combobox = {
14688             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14689             cn: [
14690                 box
14691             ]
14692         };
14693         
14694         if(!this.multiple && this.showToggleBtn){
14695             
14696             var caret = {
14697                         tag: 'span',
14698                         cls: 'caret'
14699             };
14700             
14701             if (this.caret != false) {
14702                 caret = {
14703                      tag: 'i',
14704                      cls: 'fa fa-' + this.caret
14705                 };
14706                 
14707             }
14708             
14709             combobox.cn.push({
14710                 tag :'span',
14711                 cls : 'input-group-addon btn dropdown-toggle',
14712                 cn : [
14713                     caret,
14714                     {
14715                         tag: 'span',
14716                         cls: 'combobox-clear',
14717                         cn  : [
14718                             {
14719                                 tag : 'i',
14720                                 cls: 'icon-remove'
14721                             }
14722                         ]
14723                     }
14724                 ]
14725
14726             })
14727         }
14728         
14729         if(this.multiple){
14730             combobox.cls += ' roo-select2-container-multi';
14731         }
14732         
14733         var align = this.labelAlign || this.parentLabelAlign();
14734         
14735         if (align ==='left' && this.fieldLabel.length) {
14736
14737             cfg.cn = [
14738                 {
14739                    tag : 'i',
14740                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14741                    tooltip : 'This field is required'
14742                 },
14743                 {
14744                     tag: 'label',
14745                     cls : 'control-label',
14746                     html : this.fieldLabel
14747
14748                 },
14749                 {
14750                     cls : '', 
14751                     cn: [
14752                         combobox
14753                     ]
14754                 }
14755             ];
14756             
14757             var labelCfg = cfg.cn[1];
14758             var contentCfg = cfg.cn[2];
14759             
14760
14761             if(this.indicatorpos == 'right'){
14762                 cfg.cn = [
14763                     {
14764                         tag: 'label',
14765                         'for' :  id,
14766                         cls : 'control-label',
14767                         cn : [
14768                             {
14769                                 tag : 'span',
14770                                 html : this.fieldLabel
14771                             },
14772                             {
14773                                 tag : 'i',
14774                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14775                                 tooltip : 'This field is required'
14776                             }
14777                         ]
14778                     },
14779                     {
14780                         cls : "",
14781                         cn: [
14782                             combobox
14783                         ]
14784                     }
14785
14786                 ];
14787                 
14788                 labelCfg = cfg.cn[0];
14789                 contentCfg = cfg.cn[1];
14790             }
14791             
14792            
14793             
14794             if(this.labelWidth > 12){
14795                 labelCfg.style = "width: " + this.labelWidth + 'px';
14796             }
14797             
14798             if(this.labelWidth < 13 && this.labelmd == 0){
14799                 this.labelmd = this.labelWidth;
14800             }
14801             
14802             if(this.labellg > 0){
14803                 labelCfg.cls += ' col-lg-' + this.labellg;
14804                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14805             }
14806             
14807             if(this.labelmd > 0){
14808                 labelCfg.cls += ' col-md-' + this.labelmd;
14809                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14810             }
14811             
14812             if(this.labelsm > 0){
14813                 labelCfg.cls += ' col-sm-' + this.labelsm;
14814                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14815             }
14816             
14817             if(this.labelxs > 0){
14818                 labelCfg.cls += ' col-xs-' + this.labelxs;
14819                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14820             }
14821                 
14822                 
14823         } else if ( this.fieldLabel.length) {
14824             cfg.cn = [
14825                 {
14826                    tag : 'i',
14827                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14828                    tooltip : 'This field is required'
14829                 },
14830                 {
14831                     tag: 'label',
14832                     cls : 'control-label',
14833                     html : this.fieldLabel
14834
14835                 },
14836                 {
14837                     cls : '', 
14838                     cn: [
14839                         combobox
14840                     ]
14841                 }
14842             ];
14843             
14844             if(this.indicatorpos == 'right'){
14845                 cfg.cn = [
14846                     {
14847                         tag: 'label',
14848                         cls : 'control-label',
14849                         html : this.fieldLabel,
14850                         cn : [
14851                             {
14852                                tag : 'i',
14853                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14854                                tooltip : 'This field is required'
14855                             }
14856                         ]
14857                     },
14858                     {
14859                         cls : '', 
14860                         cn: [
14861                             combobox
14862                         ]
14863                     }
14864                 ];
14865             }
14866         } else {
14867             cfg.cn = combobox;    
14868         }
14869         
14870         
14871         var settings = this;
14872         
14873         ['xs','sm','md','lg'].map(function(size){
14874             if (settings[size]) {
14875                 cfg.cls += ' col-' + size + '-' + settings[size];
14876             }
14877         });
14878         
14879         return cfg;
14880     },
14881     
14882     initTouchView : function()
14883     {
14884         this.renderTouchView();
14885         
14886         this.touchViewEl.on('scroll', function(){
14887             this.el.dom.scrollTop = 0;
14888         }, this);
14889         
14890         this.originalValue = this.getValue();
14891         
14892         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14893         
14894         this.inputEl().on("click", this.showTouchView, this);
14895         if (this.triggerEl) {
14896             this.triggerEl.on("click", this.showTouchView, this);
14897         }
14898         
14899         
14900         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14901         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14902         
14903         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14904         
14905         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14906         this.store.on('load', this.onTouchViewLoad, this);
14907         this.store.on('loadexception', this.onTouchViewLoadException, this);
14908         
14909         if(this.hiddenName){
14910             
14911             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14912             
14913             this.hiddenField.dom.value =
14914                 this.hiddenValue !== undefined ? this.hiddenValue :
14915                 this.value !== undefined ? this.value : '';
14916         
14917             this.el.dom.removeAttribute('name');
14918             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14919         }
14920         
14921         if(this.multiple){
14922             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14923             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14924         }
14925         
14926         if(this.removable && !this.multiple){
14927             var close = this.closeTriggerEl();
14928             if(close){
14929                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14930                 close.on('click', this.removeBtnClick, this, close);
14931             }
14932         }
14933         /*
14934          * fix the bug in Safari iOS8
14935          */
14936         this.inputEl().on("focus", function(e){
14937             document.activeElement.blur();
14938         }, this);
14939         
14940         return;
14941         
14942         
14943     },
14944     
14945     renderTouchView : function()
14946     {
14947         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14948         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14949         
14950         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14951         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14952         
14953         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14954         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14955         this.touchViewBodyEl.setStyle('overflow', 'auto');
14956         
14957         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14958         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14959         
14960         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14961         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14962         
14963     },
14964     
14965     showTouchView : function()
14966     {
14967         if(this.disabled){
14968             return;
14969         }
14970         
14971         this.touchViewHeaderEl.hide();
14972
14973         if(this.modalTitle.length){
14974             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14975             this.touchViewHeaderEl.show();
14976         }
14977
14978         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14979         this.touchViewEl.show();
14980
14981         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14982         
14983         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14984         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14985
14986         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14987
14988         if(this.modalTitle.length){
14989             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14990         }
14991         
14992         this.touchViewBodyEl.setHeight(bodyHeight);
14993
14994         if(this.animate){
14995             var _this = this;
14996             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14997         }else{
14998             this.touchViewEl.addClass('in');
14999         }
15000
15001         this.doTouchViewQuery();
15002         
15003     },
15004     
15005     hideTouchView : function()
15006     {
15007         this.touchViewEl.removeClass('in');
15008
15009         if(this.animate){
15010             var _this = this;
15011             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15012         }else{
15013             this.touchViewEl.setStyle('display', 'none');
15014         }
15015         
15016     },
15017     
15018     setTouchViewValue : function()
15019     {
15020         if(this.multiple){
15021             this.clearItem();
15022         
15023             var _this = this;
15024
15025             Roo.each(this.tickItems, function(o){
15026                 this.addItem(o);
15027             }, this);
15028         }
15029         
15030         this.hideTouchView();
15031     },
15032     
15033     doTouchViewQuery : function()
15034     {
15035         var qe = {
15036             query: '',
15037             forceAll: true,
15038             combo: this,
15039             cancel:false
15040         };
15041         
15042         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15043             return false;
15044         }
15045         
15046         if(!this.alwaysQuery || this.mode == 'local'){
15047             this.onTouchViewLoad();
15048             return;
15049         }
15050         
15051         this.store.load();
15052     },
15053     
15054     onTouchViewBeforeLoad : function(combo,opts)
15055     {
15056         return;
15057     },
15058
15059     // private
15060     onTouchViewLoad : function()
15061     {
15062         if(this.store.getCount() < 1){
15063             this.onTouchViewEmptyResults();
15064             return;
15065         }
15066         
15067         this.clearTouchView();
15068         
15069         var rawValue = this.getRawValue();
15070         
15071         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15072         
15073         this.tickItems = [];
15074         
15075         this.store.data.each(function(d, rowIndex){
15076             var row = this.touchViewListGroup.createChild(template);
15077             
15078             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15079                 row.addClass(d.data.cls);
15080             }
15081             
15082             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15083                 var cfg = {
15084                     data : d.data,
15085                     html : d.data[this.displayField]
15086                 };
15087                 
15088                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15089                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15090                 }
15091             }
15092             row.removeClass('selected');
15093             if(!this.multiple && this.valueField &&
15094                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15095             {
15096                 // radio buttons..
15097                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15098                 row.addClass('selected');
15099             }
15100             
15101             if(this.multiple && this.valueField &&
15102                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15103             {
15104                 
15105                 // checkboxes...
15106                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15107                 this.tickItems.push(d.data);
15108             }
15109             
15110             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15111             
15112         }, this);
15113         
15114         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15115         
15116         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15117
15118         if(this.modalTitle.length){
15119             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15120         }
15121
15122         var listHeight = this.touchViewListGroup.getHeight();
15123         
15124         var _this = this;
15125         
15126         if(firstChecked && listHeight > bodyHeight){
15127             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15128         }
15129         
15130     },
15131     
15132     onTouchViewLoadException : function()
15133     {
15134         this.hideTouchView();
15135     },
15136     
15137     onTouchViewEmptyResults : function()
15138     {
15139         this.clearTouchView();
15140         
15141         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15142         
15143         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15144         
15145     },
15146     
15147     clearTouchView : function()
15148     {
15149         this.touchViewListGroup.dom.innerHTML = '';
15150     },
15151     
15152     onTouchViewClick : function(e, el, o)
15153     {
15154         e.preventDefault();
15155         
15156         var row = o.row;
15157         var rowIndex = o.rowIndex;
15158         
15159         var r = this.store.getAt(rowIndex);
15160         
15161         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15162             
15163             if(!this.multiple){
15164                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15165                     c.dom.removeAttribute('checked');
15166                 }, this);
15167
15168                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15169
15170                 this.setFromData(r.data);
15171
15172                 var close = this.closeTriggerEl();
15173
15174                 if(close){
15175                     close.show();
15176                 }
15177
15178                 this.hideTouchView();
15179
15180                 this.fireEvent('select', this, r, rowIndex);
15181
15182                 return;
15183             }
15184
15185             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15186                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15187                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15188                 return;
15189             }
15190
15191             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15192             this.addItem(r.data);
15193             this.tickItems.push(r.data);
15194         }
15195     },
15196     
15197     getAutoCreateNativeIOS : function()
15198     {
15199         var cfg = {
15200             cls: 'form-group' //input-group,
15201         };
15202         
15203         var combobox =  {
15204             tag: 'select',
15205             cls : 'roo-ios-select'
15206         };
15207         
15208         if (this.name) {
15209             combobox.name = this.name;
15210         }
15211         
15212         if (this.disabled) {
15213             combobox.disabled = true;
15214         }
15215         
15216         var settings = this;
15217         
15218         ['xs','sm','md','lg'].map(function(size){
15219             if (settings[size]) {
15220                 cfg.cls += ' col-' + size + '-' + settings[size];
15221             }
15222         });
15223         
15224         cfg.cn = combobox;
15225         
15226         return cfg;
15227         
15228     },
15229     
15230     initIOSView : function()
15231     {
15232         this.store.on('load', this.onIOSViewLoad, this);
15233         
15234         return;
15235     },
15236     
15237     onIOSViewLoad : function()
15238     {
15239         if(this.store.getCount() < 1){
15240             return;
15241         }
15242         
15243         this.clearIOSView();
15244         
15245         if(this.allowBlank) {
15246             
15247             var default_text = '-- SELECT --';
15248             
15249             if(this.placeholder.length){
15250                 default_text = this.placeholder;
15251             }
15252             
15253             if(this.emptyTitle.length){
15254                 default_text += ' - ' + this.emptyTitle + ' -';
15255             }
15256             
15257             var opt = this.inputEl().createChild({
15258                 tag: 'option',
15259                 value : 0,
15260                 html : default_text
15261             });
15262             
15263             var o = {};
15264             o[this.valueField] = 0;
15265             o[this.displayField] = default_text;
15266             
15267             this.ios_options.push({
15268                 data : o,
15269                 el : opt
15270             });
15271             
15272         }
15273         
15274         this.store.data.each(function(d, rowIndex){
15275             
15276             var html = '';
15277             
15278             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15279                 html = d.data[this.displayField];
15280             }
15281             
15282             var value = '';
15283             
15284             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15285                 value = d.data[this.valueField];
15286             }
15287             
15288             var option = {
15289                 tag: 'option',
15290                 value : value,
15291                 html : html
15292             };
15293             
15294             if(this.value == d.data[this.valueField]){
15295                 option['selected'] = true;
15296             }
15297             
15298             var opt = this.inputEl().createChild(option);
15299             
15300             this.ios_options.push({
15301                 data : d.data,
15302                 el : opt
15303             });
15304             
15305         }, this);
15306         
15307         this.inputEl().on('change', function(){
15308            this.fireEvent('select', this);
15309         }, this);
15310         
15311     },
15312     
15313     clearIOSView: function()
15314     {
15315         this.inputEl().dom.innerHTML = '';
15316         
15317         this.ios_options = [];
15318     },
15319     
15320     setIOSValue: function(v)
15321     {
15322         this.value = v;
15323         
15324         if(!this.ios_options){
15325             return;
15326         }
15327         
15328         Roo.each(this.ios_options, function(opts){
15329            
15330            opts.el.dom.removeAttribute('selected');
15331            
15332            if(opts.data[this.valueField] != v){
15333                return;
15334            }
15335            
15336            opts.el.dom.setAttribute('selected', true);
15337            
15338         }, this);
15339     }
15340
15341     /** 
15342     * @cfg {Boolean} grow 
15343     * @hide 
15344     */
15345     /** 
15346     * @cfg {Number} growMin 
15347     * @hide 
15348     */
15349     /** 
15350     * @cfg {Number} growMax 
15351     * @hide 
15352     */
15353     /**
15354      * @hide
15355      * @method autoSize
15356      */
15357 });
15358
15359 Roo.apply(Roo.bootstrap.ComboBox,  {
15360     
15361     header : {
15362         tag: 'div',
15363         cls: 'modal-header',
15364         cn: [
15365             {
15366                 tag: 'h4',
15367                 cls: 'modal-title'
15368             }
15369         ]
15370     },
15371     
15372     body : {
15373         tag: 'div',
15374         cls: 'modal-body',
15375         cn: [
15376             {
15377                 tag: 'ul',
15378                 cls: 'list-group'
15379             }
15380         ]
15381     },
15382     
15383     listItemRadio : {
15384         tag: 'li',
15385         cls: 'list-group-item',
15386         cn: [
15387             {
15388                 tag: 'span',
15389                 cls: 'roo-combobox-list-group-item-value'
15390             },
15391             {
15392                 tag: 'div',
15393                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15394                 cn: [
15395                     {
15396                         tag: 'input',
15397                         type: 'radio'
15398                     },
15399                     {
15400                         tag: 'label'
15401                     }
15402                 ]
15403             }
15404         ]
15405     },
15406     
15407     listItemCheckbox : {
15408         tag: 'li',
15409         cls: 'list-group-item',
15410         cn: [
15411             {
15412                 tag: 'span',
15413                 cls: 'roo-combobox-list-group-item-value'
15414             },
15415             {
15416                 tag: 'div',
15417                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15418                 cn: [
15419                     {
15420                         tag: 'input',
15421                         type: 'checkbox'
15422                     },
15423                     {
15424                         tag: 'label'
15425                     }
15426                 ]
15427             }
15428         ]
15429     },
15430     
15431     emptyResult : {
15432         tag: 'div',
15433         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15434     },
15435     
15436     footer : {
15437         tag: 'div',
15438         cls: 'modal-footer',
15439         cn: [
15440             {
15441                 tag: 'div',
15442                 cls: 'row',
15443                 cn: [
15444                     {
15445                         tag: 'div',
15446                         cls: 'col-xs-6 text-left',
15447                         cn: {
15448                             tag: 'button',
15449                             cls: 'btn btn-danger roo-touch-view-cancel',
15450                             html: 'Cancel'
15451                         }
15452                     },
15453                     {
15454                         tag: 'div',
15455                         cls: 'col-xs-6 text-right',
15456                         cn: {
15457                             tag: 'button',
15458                             cls: 'btn btn-success roo-touch-view-ok',
15459                             html: 'OK'
15460                         }
15461                     }
15462                 ]
15463             }
15464         ]
15465         
15466     }
15467 });
15468
15469 Roo.apply(Roo.bootstrap.ComboBox,  {
15470     
15471     touchViewTemplate : {
15472         tag: 'div',
15473         cls: 'modal fade roo-combobox-touch-view',
15474         cn: [
15475             {
15476                 tag: 'div',
15477                 cls: 'modal-dialog',
15478                 style : 'position:fixed', // we have to fix position....
15479                 cn: [
15480                     {
15481                         tag: 'div',
15482                         cls: 'modal-content',
15483                         cn: [
15484                             Roo.bootstrap.ComboBox.header,
15485                             Roo.bootstrap.ComboBox.body,
15486                             Roo.bootstrap.ComboBox.footer
15487                         ]
15488                     }
15489                 ]
15490             }
15491         ]
15492     }
15493 });/*
15494  * Based on:
15495  * Ext JS Library 1.1.1
15496  * Copyright(c) 2006-2007, Ext JS, LLC.
15497  *
15498  * Originally Released Under LGPL - original licence link has changed is not relivant.
15499  *
15500  * Fork - LGPL
15501  * <script type="text/javascript">
15502  */
15503
15504 /**
15505  * @class Roo.View
15506  * @extends Roo.util.Observable
15507  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15508  * This class also supports single and multi selection modes. <br>
15509  * Create a data model bound view:
15510  <pre><code>
15511  var store = new Roo.data.Store(...);
15512
15513  var view = new Roo.View({
15514     el : "my-element",
15515     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15516  
15517     singleSelect: true,
15518     selectedClass: "ydataview-selected",
15519     store: store
15520  });
15521
15522  // listen for node click?
15523  view.on("click", function(vw, index, node, e){
15524  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15525  });
15526
15527  // load XML data
15528  dataModel.load("foobar.xml");
15529  </code></pre>
15530  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15531  * <br><br>
15532  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15533  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15534  * 
15535  * Note: old style constructor is still suported (container, template, config)
15536  * 
15537  * @constructor
15538  * Create a new View
15539  * @param {Object} config The config object
15540  * 
15541  */
15542 Roo.View = function(config, depreciated_tpl, depreciated_config){
15543     
15544     this.parent = false;
15545     
15546     if (typeof(depreciated_tpl) == 'undefined') {
15547         // new way.. - universal constructor.
15548         Roo.apply(this, config);
15549         this.el  = Roo.get(this.el);
15550     } else {
15551         // old format..
15552         this.el  = Roo.get(config);
15553         this.tpl = depreciated_tpl;
15554         Roo.apply(this, depreciated_config);
15555     }
15556     this.wrapEl  = this.el.wrap().wrap();
15557     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15558     
15559     
15560     if(typeof(this.tpl) == "string"){
15561         this.tpl = new Roo.Template(this.tpl);
15562     } else {
15563         // support xtype ctors..
15564         this.tpl = new Roo.factory(this.tpl, Roo);
15565     }
15566     
15567     
15568     this.tpl.compile();
15569     
15570     /** @private */
15571     this.addEvents({
15572         /**
15573          * @event beforeclick
15574          * Fires before a click is processed. Returns false to cancel the default action.
15575          * @param {Roo.View} this
15576          * @param {Number} index The index of the target node
15577          * @param {HTMLElement} node The target node
15578          * @param {Roo.EventObject} e The raw event object
15579          */
15580             "beforeclick" : true,
15581         /**
15582          * @event click
15583          * Fires when a template node is clicked.
15584          * @param {Roo.View} this
15585          * @param {Number} index The index of the target node
15586          * @param {HTMLElement} node The target node
15587          * @param {Roo.EventObject} e The raw event object
15588          */
15589             "click" : true,
15590         /**
15591          * @event dblclick
15592          * Fires when a template node is double clicked.
15593          * @param {Roo.View} this
15594          * @param {Number} index The index of the target node
15595          * @param {HTMLElement} node The target node
15596          * @param {Roo.EventObject} e The raw event object
15597          */
15598             "dblclick" : true,
15599         /**
15600          * @event contextmenu
15601          * Fires when a template node is right clicked.
15602          * @param {Roo.View} this
15603          * @param {Number} index The index of the target node
15604          * @param {HTMLElement} node The target node
15605          * @param {Roo.EventObject} e The raw event object
15606          */
15607             "contextmenu" : true,
15608         /**
15609          * @event selectionchange
15610          * Fires when the selected nodes change.
15611          * @param {Roo.View} this
15612          * @param {Array} selections Array of the selected nodes
15613          */
15614             "selectionchange" : true,
15615     
15616         /**
15617          * @event beforeselect
15618          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15619          * @param {Roo.View} this
15620          * @param {HTMLElement} node The node to be selected
15621          * @param {Array} selections Array of currently selected nodes
15622          */
15623             "beforeselect" : true,
15624         /**
15625          * @event preparedata
15626          * Fires on every row to render, to allow you to change the data.
15627          * @param {Roo.View} this
15628          * @param {Object} data to be rendered (change this)
15629          */
15630           "preparedata" : true
15631           
15632           
15633         });
15634
15635
15636
15637     this.el.on({
15638         "click": this.onClick,
15639         "dblclick": this.onDblClick,
15640         "contextmenu": this.onContextMenu,
15641         scope:this
15642     });
15643
15644     this.selections = [];
15645     this.nodes = [];
15646     this.cmp = new Roo.CompositeElementLite([]);
15647     if(this.store){
15648         this.store = Roo.factory(this.store, Roo.data);
15649         this.setStore(this.store, true);
15650     }
15651     
15652     if ( this.footer && this.footer.xtype) {
15653            
15654          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15655         
15656         this.footer.dataSource = this.store;
15657         this.footer.container = fctr;
15658         this.footer = Roo.factory(this.footer, Roo);
15659         fctr.insertFirst(this.el);
15660         
15661         // this is a bit insane - as the paging toolbar seems to detach the el..
15662 //        dom.parentNode.parentNode.parentNode
15663          // they get detached?
15664     }
15665     
15666     
15667     Roo.View.superclass.constructor.call(this);
15668     
15669     
15670 };
15671
15672 Roo.extend(Roo.View, Roo.util.Observable, {
15673     
15674      /**
15675      * @cfg {Roo.data.Store} store Data store to load data from.
15676      */
15677     store : false,
15678     
15679     /**
15680      * @cfg {String|Roo.Element} el The container element.
15681      */
15682     el : '',
15683     
15684     /**
15685      * @cfg {String|Roo.Template} tpl The template used by this View 
15686      */
15687     tpl : false,
15688     /**
15689      * @cfg {String} dataName the named area of the template to use as the data area
15690      *                          Works with domtemplates roo-name="name"
15691      */
15692     dataName: false,
15693     /**
15694      * @cfg {String} selectedClass The css class to add to selected nodes
15695      */
15696     selectedClass : "x-view-selected",
15697      /**
15698      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15699      */
15700     emptyText : "",
15701     
15702     /**
15703      * @cfg {String} text to display on mask (default Loading)
15704      */
15705     mask : false,
15706     /**
15707      * @cfg {Boolean} multiSelect Allow multiple selection
15708      */
15709     multiSelect : false,
15710     /**
15711      * @cfg {Boolean} singleSelect Allow single selection
15712      */
15713     singleSelect:  false,
15714     
15715     /**
15716      * @cfg {Boolean} toggleSelect - selecting 
15717      */
15718     toggleSelect : false,
15719     
15720     /**
15721      * @cfg {Boolean} tickable - selecting 
15722      */
15723     tickable : false,
15724     
15725     /**
15726      * Returns the element this view is bound to.
15727      * @return {Roo.Element}
15728      */
15729     getEl : function(){
15730         return this.wrapEl;
15731     },
15732     
15733     
15734
15735     /**
15736      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15737      */
15738     refresh : function(){
15739         //Roo.log('refresh');
15740         var t = this.tpl;
15741         
15742         // if we are using something like 'domtemplate', then
15743         // the what gets used is:
15744         // t.applySubtemplate(NAME, data, wrapping data..)
15745         // the outer template then get' applied with
15746         //     the store 'extra data'
15747         // and the body get's added to the
15748         //      roo-name="data" node?
15749         //      <span class='roo-tpl-{name}'></span> ?????
15750         
15751         
15752         
15753         this.clearSelections();
15754         this.el.update("");
15755         var html = [];
15756         var records = this.store.getRange();
15757         if(records.length < 1) {
15758             
15759             // is this valid??  = should it render a template??
15760             
15761             this.el.update(this.emptyText);
15762             return;
15763         }
15764         var el = this.el;
15765         if (this.dataName) {
15766             this.el.update(t.apply(this.store.meta)); //????
15767             el = this.el.child('.roo-tpl-' + this.dataName);
15768         }
15769         
15770         for(var i = 0, len = records.length; i < len; i++){
15771             var data = this.prepareData(records[i].data, i, records[i]);
15772             this.fireEvent("preparedata", this, data, i, records[i]);
15773             
15774             var d = Roo.apply({}, data);
15775             
15776             if(this.tickable){
15777                 Roo.apply(d, {'roo-id' : Roo.id()});
15778                 
15779                 var _this = this;
15780             
15781                 Roo.each(this.parent.item, function(item){
15782                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15783                         return;
15784                     }
15785                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15786                 });
15787             }
15788             
15789             html[html.length] = Roo.util.Format.trim(
15790                 this.dataName ?
15791                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15792                     t.apply(d)
15793             );
15794         }
15795         
15796         
15797         
15798         el.update(html.join(""));
15799         this.nodes = el.dom.childNodes;
15800         this.updateIndexes(0);
15801     },
15802     
15803
15804     /**
15805      * Function to override to reformat the data that is sent to
15806      * the template for each node.
15807      * DEPRICATED - use the preparedata event handler.
15808      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15809      * a JSON object for an UpdateManager bound view).
15810      */
15811     prepareData : function(data, index, record)
15812     {
15813         this.fireEvent("preparedata", this, data, index, record);
15814         return data;
15815     },
15816
15817     onUpdate : function(ds, record){
15818         // Roo.log('on update');   
15819         this.clearSelections();
15820         var index = this.store.indexOf(record);
15821         var n = this.nodes[index];
15822         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15823         n.parentNode.removeChild(n);
15824         this.updateIndexes(index, index);
15825     },
15826
15827     
15828     
15829 // --------- FIXME     
15830     onAdd : function(ds, records, index)
15831     {
15832         //Roo.log(['on Add', ds, records, index] );        
15833         this.clearSelections();
15834         if(this.nodes.length == 0){
15835             this.refresh();
15836             return;
15837         }
15838         var n = this.nodes[index];
15839         for(var i = 0, len = records.length; i < len; i++){
15840             var d = this.prepareData(records[i].data, i, records[i]);
15841             if(n){
15842                 this.tpl.insertBefore(n, d);
15843             }else{
15844                 
15845                 this.tpl.append(this.el, d);
15846             }
15847         }
15848         this.updateIndexes(index);
15849     },
15850
15851     onRemove : function(ds, record, index){
15852        // Roo.log('onRemove');
15853         this.clearSelections();
15854         var el = this.dataName  ?
15855             this.el.child('.roo-tpl-' + this.dataName) :
15856             this.el; 
15857         
15858         el.dom.removeChild(this.nodes[index]);
15859         this.updateIndexes(index);
15860     },
15861
15862     /**
15863      * Refresh an individual node.
15864      * @param {Number} index
15865      */
15866     refreshNode : function(index){
15867         this.onUpdate(this.store, this.store.getAt(index));
15868     },
15869
15870     updateIndexes : function(startIndex, endIndex){
15871         var ns = this.nodes;
15872         startIndex = startIndex || 0;
15873         endIndex = endIndex || ns.length - 1;
15874         for(var i = startIndex; i <= endIndex; i++){
15875             ns[i].nodeIndex = i;
15876         }
15877     },
15878
15879     /**
15880      * Changes the data store this view uses and refresh the view.
15881      * @param {Store} store
15882      */
15883     setStore : function(store, initial){
15884         if(!initial && this.store){
15885             this.store.un("datachanged", this.refresh);
15886             this.store.un("add", this.onAdd);
15887             this.store.un("remove", this.onRemove);
15888             this.store.un("update", this.onUpdate);
15889             this.store.un("clear", this.refresh);
15890             this.store.un("beforeload", this.onBeforeLoad);
15891             this.store.un("load", this.onLoad);
15892             this.store.un("loadexception", this.onLoad);
15893         }
15894         if(store){
15895           
15896             store.on("datachanged", this.refresh, this);
15897             store.on("add", this.onAdd, this);
15898             store.on("remove", this.onRemove, this);
15899             store.on("update", this.onUpdate, this);
15900             store.on("clear", this.refresh, this);
15901             store.on("beforeload", this.onBeforeLoad, this);
15902             store.on("load", this.onLoad, this);
15903             store.on("loadexception", this.onLoad, this);
15904         }
15905         
15906         if(store){
15907             this.refresh();
15908         }
15909     },
15910     /**
15911      * onbeforeLoad - masks the loading area.
15912      *
15913      */
15914     onBeforeLoad : function(store,opts)
15915     {
15916          //Roo.log('onBeforeLoad');   
15917         if (!opts.add) {
15918             this.el.update("");
15919         }
15920         this.el.mask(this.mask ? this.mask : "Loading" ); 
15921     },
15922     onLoad : function ()
15923     {
15924         this.el.unmask();
15925     },
15926     
15927
15928     /**
15929      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15930      * @param {HTMLElement} node
15931      * @return {HTMLElement} The template node
15932      */
15933     findItemFromChild : function(node){
15934         var el = this.dataName  ?
15935             this.el.child('.roo-tpl-' + this.dataName,true) :
15936             this.el.dom; 
15937         
15938         if(!node || node.parentNode == el){
15939                     return node;
15940             }
15941             var p = node.parentNode;
15942             while(p && p != el){
15943             if(p.parentNode == el){
15944                 return p;
15945             }
15946             p = p.parentNode;
15947         }
15948             return null;
15949     },
15950
15951     /** @ignore */
15952     onClick : function(e){
15953         var item = this.findItemFromChild(e.getTarget());
15954         if(item){
15955             var index = this.indexOf(item);
15956             if(this.onItemClick(item, index, e) !== false){
15957                 this.fireEvent("click", this, index, item, e);
15958             }
15959         }else{
15960             this.clearSelections();
15961         }
15962     },
15963
15964     /** @ignore */
15965     onContextMenu : function(e){
15966         var item = this.findItemFromChild(e.getTarget());
15967         if(item){
15968             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15969         }
15970     },
15971
15972     /** @ignore */
15973     onDblClick : function(e){
15974         var item = this.findItemFromChild(e.getTarget());
15975         if(item){
15976             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15977         }
15978     },
15979
15980     onItemClick : function(item, index, e)
15981     {
15982         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15983             return false;
15984         }
15985         if (this.toggleSelect) {
15986             var m = this.isSelected(item) ? 'unselect' : 'select';
15987             //Roo.log(m);
15988             var _t = this;
15989             _t[m](item, true, false);
15990             return true;
15991         }
15992         if(this.multiSelect || this.singleSelect){
15993             if(this.multiSelect && e.shiftKey && this.lastSelection){
15994                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15995             }else{
15996                 this.select(item, this.multiSelect && e.ctrlKey);
15997                 this.lastSelection = item;
15998             }
15999             
16000             if(!this.tickable){
16001                 e.preventDefault();
16002             }
16003             
16004         }
16005         return true;
16006     },
16007
16008     /**
16009      * Get the number of selected nodes.
16010      * @return {Number}
16011      */
16012     getSelectionCount : function(){
16013         return this.selections.length;
16014     },
16015
16016     /**
16017      * Get the currently selected nodes.
16018      * @return {Array} An array of HTMLElements
16019      */
16020     getSelectedNodes : function(){
16021         return this.selections;
16022     },
16023
16024     /**
16025      * Get the indexes of the selected nodes.
16026      * @return {Array}
16027      */
16028     getSelectedIndexes : function(){
16029         var indexes = [], s = this.selections;
16030         for(var i = 0, len = s.length; i < len; i++){
16031             indexes.push(s[i].nodeIndex);
16032         }
16033         return indexes;
16034     },
16035
16036     /**
16037      * Clear all selections
16038      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16039      */
16040     clearSelections : function(suppressEvent){
16041         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16042             this.cmp.elements = this.selections;
16043             this.cmp.removeClass(this.selectedClass);
16044             this.selections = [];
16045             if(!suppressEvent){
16046                 this.fireEvent("selectionchange", this, this.selections);
16047             }
16048         }
16049     },
16050
16051     /**
16052      * Returns true if the passed node is selected
16053      * @param {HTMLElement/Number} node The node or node index
16054      * @return {Boolean}
16055      */
16056     isSelected : function(node){
16057         var s = this.selections;
16058         if(s.length < 1){
16059             return false;
16060         }
16061         node = this.getNode(node);
16062         return s.indexOf(node) !== -1;
16063     },
16064
16065     /**
16066      * Selects nodes.
16067      * @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
16068      * @param {Boolean} keepExisting (optional) true to keep existing selections
16069      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16070      */
16071     select : function(nodeInfo, keepExisting, suppressEvent){
16072         if(nodeInfo instanceof Array){
16073             if(!keepExisting){
16074                 this.clearSelections(true);
16075             }
16076             for(var i = 0, len = nodeInfo.length; i < len; i++){
16077                 this.select(nodeInfo[i], true, true);
16078             }
16079             return;
16080         } 
16081         var node = this.getNode(nodeInfo);
16082         if(!node || this.isSelected(node)){
16083             return; // already selected.
16084         }
16085         if(!keepExisting){
16086             this.clearSelections(true);
16087         }
16088         
16089         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16090             Roo.fly(node).addClass(this.selectedClass);
16091             this.selections.push(node);
16092             if(!suppressEvent){
16093                 this.fireEvent("selectionchange", this, this.selections);
16094             }
16095         }
16096         
16097         
16098     },
16099       /**
16100      * Unselects nodes.
16101      * @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
16102      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16103      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16104      */
16105     unselect : function(nodeInfo, keepExisting, suppressEvent)
16106     {
16107         if(nodeInfo instanceof Array){
16108             Roo.each(this.selections, function(s) {
16109                 this.unselect(s, nodeInfo);
16110             }, this);
16111             return;
16112         }
16113         var node = this.getNode(nodeInfo);
16114         if(!node || !this.isSelected(node)){
16115             //Roo.log("not selected");
16116             return; // not selected.
16117         }
16118         // fireevent???
16119         var ns = [];
16120         Roo.each(this.selections, function(s) {
16121             if (s == node ) {
16122                 Roo.fly(node).removeClass(this.selectedClass);
16123
16124                 return;
16125             }
16126             ns.push(s);
16127         },this);
16128         
16129         this.selections= ns;
16130         this.fireEvent("selectionchange", this, this.selections);
16131     },
16132
16133     /**
16134      * Gets a template node.
16135      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16136      * @return {HTMLElement} The node or null if it wasn't found
16137      */
16138     getNode : function(nodeInfo){
16139         if(typeof nodeInfo == "string"){
16140             return document.getElementById(nodeInfo);
16141         }else if(typeof nodeInfo == "number"){
16142             return this.nodes[nodeInfo];
16143         }
16144         return nodeInfo;
16145     },
16146
16147     /**
16148      * Gets a range template nodes.
16149      * @param {Number} startIndex
16150      * @param {Number} endIndex
16151      * @return {Array} An array of nodes
16152      */
16153     getNodes : function(start, end){
16154         var ns = this.nodes;
16155         start = start || 0;
16156         end = typeof end == "undefined" ? ns.length - 1 : end;
16157         var nodes = [];
16158         if(start <= end){
16159             for(var i = start; i <= end; i++){
16160                 nodes.push(ns[i]);
16161             }
16162         } else{
16163             for(var i = start; i >= end; i--){
16164                 nodes.push(ns[i]);
16165             }
16166         }
16167         return nodes;
16168     },
16169
16170     /**
16171      * Finds the index of the passed node
16172      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16173      * @return {Number} The index of the node or -1
16174      */
16175     indexOf : function(node){
16176         node = this.getNode(node);
16177         if(typeof node.nodeIndex == "number"){
16178             return node.nodeIndex;
16179         }
16180         var ns = this.nodes;
16181         for(var i = 0, len = ns.length; i < len; i++){
16182             if(ns[i] == node){
16183                 return i;
16184             }
16185         }
16186         return -1;
16187     }
16188 });
16189 /*
16190  * - LGPL
16191  *
16192  * based on jquery fullcalendar
16193  * 
16194  */
16195
16196 Roo.bootstrap = Roo.bootstrap || {};
16197 /**
16198  * @class Roo.bootstrap.Calendar
16199  * @extends Roo.bootstrap.Component
16200  * Bootstrap Calendar class
16201  * @cfg {Boolean} loadMask (true|false) default false
16202  * @cfg {Object} header generate the user specific header of the calendar, default false
16203
16204  * @constructor
16205  * Create a new Container
16206  * @param {Object} config The config object
16207  */
16208
16209
16210
16211 Roo.bootstrap.Calendar = function(config){
16212     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16213      this.addEvents({
16214         /**
16215              * @event select
16216              * Fires when a date is selected
16217              * @param {DatePicker} this
16218              * @param {Date} date The selected date
16219              */
16220         'select': true,
16221         /**
16222              * @event monthchange
16223              * Fires when the displayed month changes 
16224              * @param {DatePicker} this
16225              * @param {Date} date The selected month
16226              */
16227         'monthchange': true,
16228         /**
16229              * @event evententer
16230              * Fires when mouse over an event
16231              * @param {Calendar} this
16232              * @param {event} Event
16233              */
16234         'evententer': true,
16235         /**
16236              * @event eventleave
16237              * Fires when the mouse leaves an
16238              * @param {Calendar} this
16239              * @param {event}
16240              */
16241         'eventleave': true,
16242         /**
16243              * @event eventclick
16244              * Fires when the mouse click an
16245              * @param {Calendar} this
16246              * @param {event}
16247              */
16248         'eventclick': true
16249         
16250     });
16251
16252 };
16253
16254 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16255     
16256      /**
16257      * @cfg {Number} startDay
16258      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16259      */
16260     startDay : 0,
16261     
16262     loadMask : false,
16263     
16264     header : false,
16265       
16266     getAutoCreate : function(){
16267         
16268         
16269         var fc_button = function(name, corner, style, content ) {
16270             return Roo.apply({},{
16271                 tag : 'span',
16272                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16273                          (corner.length ?
16274                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16275                             ''
16276                         ),
16277                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16278                 unselectable: 'on'
16279             });
16280         };
16281         
16282         var header = {};
16283         
16284         if(!this.header){
16285             header = {
16286                 tag : 'table',
16287                 cls : 'fc-header',
16288                 style : 'width:100%',
16289                 cn : [
16290                     {
16291                         tag: 'tr',
16292                         cn : [
16293                             {
16294                                 tag : 'td',
16295                                 cls : 'fc-header-left',
16296                                 cn : [
16297                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16298                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16299                                     { tag: 'span', cls: 'fc-header-space' },
16300                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16301
16302
16303                                 ]
16304                             },
16305
16306                             {
16307                                 tag : 'td',
16308                                 cls : 'fc-header-center',
16309                                 cn : [
16310                                     {
16311                                         tag: 'span',
16312                                         cls: 'fc-header-title',
16313                                         cn : {
16314                                             tag: 'H2',
16315                                             html : 'month / year'
16316                                         }
16317                                     }
16318
16319                                 ]
16320                             },
16321                             {
16322                                 tag : 'td',
16323                                 cls : 'fc-header-right',
16324                                 cn : [
16325                               /*      fc_button('month', 'left', '', 'month' ),
16326                                     fc_button('week', '', '', 'week' ),
16327                                     fc_button('day', 'right', '', 'day' )
16328                                 */    
16329
16330                                 ]
16331                             }
16332
16333                         ]
16334                     }
16335                 ]
16336             };
16337         }
16338         
16339         header = this.header;
16340         
16341        
16342         var cal_heads = function() {
16343             var ret = [];
16344             // fixme - handle this.
16345             
16346             for (var i =0; i < Date.dayNames.length; i++) {
16347                 var d = Date.dayNames[i];
16348                 ret.push({
16349                     tag: 'th',
16350                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16351                     html : d.substring(0,3)
16352                 });
16353                 
16354             }
16355             ret[0].cls += ' fc-first';
16356             ret[6].cls += ' fc-last';
16357             return ret;
16358         };
16359         var cal_cell = function(n) {
16360             return  {
16361                 tag: 'td',
16362                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16363                 cn : [
16364                     {
16365                         cn : [
16366                             {
16367                                 cls: 'fc-day-number',
16368                                 html: 'D'
16369                             },
16370                             {
16371                                 cls: 'fc-day-content',
16372                              
16373                                 cn : [
16374                                      {
16375                                         style: 'position: relative;' // height: 17px;
16376                                     }
16377                                 ]
16378                             }
16379                             
16380                             
16381                         ]
16382                     }
16383                 ]
16384                 
16385             }
16386         };
16387         var cal_rows = function() {
16388             
16389             var ret = [];
16390             for (var r = 0; r < 6; r++) {
16391                 var row= {
16392                     tag : 'tr',
16393                     cls : 'fc-week',
16394                     cn : []
16395                 };
16396                 
16397                 for (var i =0; i < Date.dayNames.length; i++) {
16398                     var d = Date.dayNames[i];
16399                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16400
16401                 }
16402                 row.cn[0].cls+=' fc-first';
16403                 row.cn[0].cn[0].style = 'min-height:90px';
16404                 row.cn[6].cls+=' fc-last';
16405                 ret.push(row);
16406                 
16407             }
16408             ret[0].cls += ' fc-first';
16409             ret[4].cls += ' fc-prev-last';
16410             ret[5].cls += ' fc-last';
16411             return ret;
16412             
16413         };
16414         
16415         var cal_table = {
16416             tag: 'table',
16417             cls: 'fc-border-separate',
16418             style : 'width:100%',
16419             cellspacing  : 0,
16420             cn : [
16421                 { 
16422                     tag: 'thead',
16423                     cn : [
16424                         { 
16425                             tag: 'tr',
16426                             cls : 'fc-first fc-last',
16427                             cn : cal_heads()
16428                         }
16429                     ]
16430                 },
16431                 { 
16432                     tag: 'tbody',
16433                     cn : cal_rows()
16434                 }
16435                   
16436             ]
16437         };
16438          
16439          var cfg = {
16440             cls : 'fc fc-ltr',
16441             cn : [
16442                 header,
16443                 {
16444                     cls : 'fc-content',
16445                     style : "position: relative;",
16446                     cn : [
16447                         {
16448                             cls : 'fc-view fc-view-month fc-grid',
16449                             style : 'position: relative',
16450                             unselectable : 'on',
16451                             cn : [
16452                                 {
16453                                     cls : 'fc-event-container',
16454                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16455                                 },
16456                                 cal_table
16457                             ]
16458                         }
16459                     ]
16460     
16461                 }
16462            ] 
16463             
16464         };
16465         
16466          
16467         
16468         return cfg;
16469     },
16470     
16471     
16472     initEvents : function()
16473     {
16474         if(!this.store){
16475             throw "can not find store for calendar";
16476         }
16477         
16478         var mark = {
16479             tag: "div",
16480             cls:"x-dlg-mask",
16481             style: "text-align:center",
16482             cn: [
16483                 {
16484                     tag: "div",
16485                     style: "background-color:white;width:50%;margin:250 auto",
16486                     cn: [
16487                         {
16488                             tag: "img",
16489                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16490                         },
16491                         {
16492                             tag: "span",
16493                             html: "Loading"
16494                         }
16495                         
16496                     ]
16497                 }
16498             ]
16499         };
16500         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16501         
16502         var size = this.el.select('.fc-content', true).first().getSize();
16503         this.maskEl.setSize(size.width, size.height);
16504         this.maskEl.enableDisplayMode("block");
16505         if(!this.loadMask){
16506             this.maskEl.hide();
16507         }
16508         
16509         this.store = Roo.factory(this.store, Roo.data);
16510         this.store.on('load', this.onLoad, this);
16511         this.store.on('beforeload', this.onBeforeLoad, this);
16512         
16513         this.resize();
16514         
16515         this.cells = this.el.select('.fc-day',true);
16516         //Roo.log(this.cells);
16517         this.textNodes = this.el.query('.fc-day-number');
16518         this.cells.addClassOnOver('fc-state-hover');
16519         
16520         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16521         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16522         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16523         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16524         
16525         this.on('monthchange', this.onMonthChange, this);
16526         
16527         this.update(new Date().clearTime());
16528     },
16529     
16530     resize : function() {
16531         var sz  = this.el.getSize();
16532         
16533         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16534         this.el.select('.fc-day-content div',true).setHeight(34);
16535     },
16536     
16537     
16538     // private
16539     showPrevMonth : function(e){
16540         this.update(this.activeDate.add("mo", -1));
16541     },
16542     showToday : function(e){
16543         this.update(new Date().clearTime());
16544     },
16545     // private
16546     showNextMonth : function(e){
16547         this.update(this.activeDate.add("mo", 1));
16548     },
16549
16550     // private
16551     showPrevYear : function(){
16552         this.update(this.activeDate.add("y", -1));
16553     },
16554
16555     // private
16556     showNextYear : function(){
16557         this.update(this.activeDate.add("y", 1));
16558     },
16559
16560     
16561    // private
16562     update : function(date)
16563     {
16564         var vd = this.activeDate;
16565         this.activeDate = date;
16566 //        if(vd && this.el){
16567 //            var t = date.getTime();
16568 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16569 //                Roo.log('using add remove');
16570 //                
16571 //                this.fireEvent('monthchange', this, date);
16572 //                
16573 //                this.cells.removeClass("fc-state-highlight");
16574 //                this.cells.each(function(c){
16575 //                   if(c.dateValue == t){
16576 //                       c.addClass("fc-state-highlight");
16577 //                       setTimeout(function(){
16578 //                            try{c.dom.firstChild.focus();}catch(e){}
16579 //                       }, 50);
16580 //                       return false;
16581 //                   }
16582 //                   return true;
16583 //                });
16584 //                return;
16585 //            }
16586 //        }
16587         
16588         var days = date.getDaysInMonth();
16589         
16590         var firstOfMonth = date.getFirstDateOfMonth();
16591         var startingPos = firstOfMonth.getDay()-this.startDay;
16592         
16593         if(startingPos < this.startDay){
16594             startingPos += 7;
16595         }
16596         
16597         var pm = date.add(Date.MONTH, -1);
16598         var prevStart = pm.getDaysInMonth()-startingPos;
16599 //        
16600         this.cells = this.el.select('.fc-day',true);
16601         this.textNodes = this.el.query('.fc-day-number');
16602         this.cells.addClassOnOver('fc-state-hover');
16603         
16604         var cells = this.cells.elements;
16605         var textEls = this.textNodes;
16606         
16607         Roo.each(cells, function(cell){
16608             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16609         });
16610         
16611         days += startingPos;
16612
16613         // convert everything to numbers so it's fast
16614         var day = 86400000;
16615         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16616         //Roo.log(d);
16617         //Roo.log(pm);
16618         //Roo.log(prevStart);
16619         
16620         var today = new Date().clearTime().getTime();
16621         var sel = date.clearTime().getTime();
16622         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16623         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16624         var ddMatch = this.disabledDatesRE;
16625         var ddText = this.disabledDatesText;
16626         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16627         var ddaysText = this.disabledDaysText;
16628         var format = this.format;
16629         
16630         var setCellClass = function(cal, cell){
16631             cell.row = 0;
16632             cell.events = [];
16633             cell.more = [];
16634             //Roo.log('set Cell Class');
16635             cell.title = "";
16636             var t = d.getTime();
16637             
16638             //Roo.log(d);
16639             
16640             cell.dateValue = t;
16641             if(t == today){
16642                 cell.className += " fc-today";
16643                 cell.className += " fc-state-highlight";
16644                 cell.title = cal.todayText;
16645             }
16646             if(t == sel){
16647                 // disable highlight in other month..
16648                 //cell.className += " fc-state-highlight";
16649                 
16650             }
16651             // disabling
16652             if(t < min) {
16653                 cell.className = " fc-state-disabled";
16654                 cell.title = cal.minText;
16655                 return;
16656             }
16657             if(t > max) {
16658                 cell.className = " fc-state-disabled";
16659                 cell.title = cal.maxText;
16660                 return;
16661             }
16662             if(ddays){
16663                 if(ddays.indexOf(d.getDay()) != -1){
16664                     cell.title = ddaysText;
16665                     cell.className = " fc-state-disabled";
16666                 }
16667             }
16668             if(ddMatch && format){
16669                 var fvalue = d.dateFormat(format);
16670                 if(ddMatch.test(fvalue)){
16671                     cell.title = ddText.replace("%0", fvalue);
16672                     cell.className = " fc-state-disabled";
16673                 }
16674             }
16675             
16676             if (!cell.initialClassName) {
16677                 cell.initialClassName = cell.dom.className;
16678             }
16679             
16680             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16681         };
16682
16683         var i = 0;
16684         
16685         for(; i < startingPos; i++) {
16686             textEls[i].innerHTML = (++prevStart);
16687             d.setDate(d.getDate()+1);
16688             
16689             cells[i].className = "fc-past fc-other-month";
16690             setCellClass(this, cells[i]);
16691         }
16692         
16693         var intDay = 0;
16694         
16695         for(; i < days; i++){
16696             intDay = i - startingPos + 1;
16697             textEls[i].innerHTML = (intDay);
16698             d.setDate(d.getDate()+1);
16699             
16700             cells[i].className = ''; // "x-date-active";
16701             setCellClass(this, cells[i]);
16702         }
16703         var extraDays = 0;
16704         
16705         for(; i < 42; i++) {
16706             textEls[i].innerHTML = (++extraDays);
16707             d.setDate(d.getDate()+1);
16708             
16709             cells[i].className = "fc-future fc-other-month";
16710             setCellClass(this, cells[i]);
16711         }
16712         
16713         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16714         
16715         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16716         
16717         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16718         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16719         
16720         if(totalRows != 6){
16721             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16722             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16723         }
16724         
16725         this.fireEvent('monthchange', this, date);
16726         
16727         
16728         /*
16729         if(!this.internalRender){
16730             var main = this.el.dom.firstChild;
16731             var w = main.offsetWidth;
16732             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16733             Roo.fly(main).setWidth(w);
16734             this.internalRender = true;
16735             // opera does not respect the auto grow header center column
16736             // then, after it gets a width opera refuses to recalculate
16737             // without a second pass
16738             if(Roo.isOpera && !this.secondPass){
16739                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16740                 this.secondPass = true;
16741                 this.update.defer(10, this, [date]);
16742             }
16743         }
16744         */
16745         
16746     },
16747     
16748     findCell : function(dt) {
16749         dt = dt.clearTime().getTime();
16750         var ret = false;
16751         this.cells.each(function(c){
16752             //Roo.log("check " +c.dateValue + '?=' + dt);
16753             if(c.dateValue == dt){
16754                 ret = c;
16755                 return false;
16756             }
16757             return true;
16758         });
16759         
16760         return ret;
16761     },
16762     
16763     findCells : function(ev) {
16764         var s = ev.start.clone().clearTime().getTime();
16765        // Roo.log(s);
16766         var e= ev.end.clone().clearTime().getTime();
16767        // Roo.log(e);
16768         var ret = [];
16769         this.cells.each(function(c){
16770              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16771             
16772             if(c.dateValue > e){
16773                 return ;
16774             }
16775             if(c.dateValue < s){
16776                 return ;
16777             }
16778             ret.push(c);
16779         });
16780         
16781         return ret;    
16782     },
16783     
16784 //    findBestRow: function(cells)
16785 //    {
16786 //        var ret = 0;
16787 //        
16788 //        for (var i =0 ; i < cells.length;i++) {
16789 //            ret  = Math.max(cells[i].rows || 0,ret);
16790 //        }
16791 //        return ret;
16792 //        
16793 //    },
16794     
16795     
16796     addItem : function(ev)
16797     {
16798         // look for vertical location slot in
16799         var cells = this.findCells(ev);
16800         
16801 //        ev.row = this.findBestRow(cells);
16802         
16803         // work out the location.
16804         
16805         var crow = false;
16806         var rows = [];
16807         for(var i =0; i < cells.length; i++) {
16808             
16809             cells[i].row = cells[0].row;
16810             
16811             if(i == 0){
16812                 cells[i].row = cells[i].row + 1;
16813             }
16814             
16815             if (!crow) {
16816                 crow = {
16817                     start : cells[i],
16818                     end :  cells[i]
16819                 };
16820                 continue;
16821             }
16822             if (crow.start.getY() == cells[i].getY()) {
16823                 // on same row.
16824                 crow.end = cells[i];
16825                 continue;
16826             }
16827             // different row.
16828             rows.push(crow);
16829             crow = {
16830                 start: cells[i],
16831                 end : cells[i]
16832             };
16833             
16834         }
16835         
16836         rows.push(crow);
16837         ev.els = [];
16838         ev.rows = rows;
16839         ev.cells = cells;
16840         
16841         cells[0].events.push(ev);
16842         
16843         this.calevents.push(ev);
16844     },
16845     
16846     clearEvents: function() {
16847         
16848         if(!this.calevents){
16849             return;
16850         }
16851         
16852         Roo.each(this.cells.elements, function(c){
16853             c.row = 0;
16854             c.events = [];
16855             c.more = [];
16856         });
16857         
16858         Roo.each(this.calevents, function(e) {
16859             Roo.each(e.els, function(el) {
16860                 el.un('mouseenter' ,this.onEventEnter, this);
16861                 el.un('mouseleave' ,this.onEventLeave, this);
16862                 el.remove();
16863             },this);
16864         },this);
16865         
16866         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16867             e.remove();
16868         });
16869         
16870     },
16871     
16872     renderEvents: function()
16873     {   
16874         var _this = this;
16875         
16876         this.cells.each(function(c) {
16877             
16878             if(c.row < 5){
16879                 return;
16880             }
16881             
16882             var ev = c.events;
16883             
16884             var r = 4;
16885             if(c.row != c.events.length){
16886                 r = 4 - (4 - (c.row - c.events.length));
16887             }
16888             
16889             c.events = ev.slice(0, r);
16890             c.more = ev.slice(r);
16891             
16892             if(c.more.length && c.more.length == 1){
16893                 c.events.push(c.more.pop());
16894             }
16895             
16896             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16897             
16898         });
16899             
16900         this.cells.each(function(c) {
16901             
16902             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16903             
16904             
16905             for (var e = 0; e < c.events.length; e++){
16906                 var ev = c.events[e];
16907                 var rows = ev.rows;
16908                 
16909                 for(var i = 0; i < rows.length; i++) {
16910                 
16911                     // how many rows should it span..
16912
16913                     var  cfg = {
16914                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16915                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16916
16917                         unselectable : "on",
16918                         cn : [
16919                             {
16920                                 cls: 'fc-event-inner',
16921                                 cn : [
16922     //                                {
16923     //                                  tag:'span',
16924     //                                  cls: 'fc-event-time',
16925     //                                  html : cells.length > 1 ? '' : ev.time
16926     //                                },
16927                                     {
16928                                       tag:'span',
16929                                       cls: 'fc-event-title',
16930                                       html : String.format('{0}', ev.title)
16931                                     }
16932
16933
16934                                 ]
16935                             },
16936                             {
16937                                 cls: 'ui-resizable-handle ui-resizable-e',
16938                                 html : '&nbsp;&nbsp;&nbsp'
16939                             }
16940
16941                         ]
16942                     };
16943
16944                     if (i == 0) {
16945                         cfg.cls += ' fc-event-start';
16946                     }
16947                     if ((i+1) == rows.length) {
16948                         cfg.cls += ' fc-event-end';
16949                     }
16950
16951                     var ctr = _this.el.select('.fc-event-container',true).first();
16952                     var cg = ctr.createChild(cfg);
16953
16954                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16955                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16956
16957                     var r = (c.more.length) ? 1 : 0;
16958                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16959                     cg.setWidth(ebox.right - sbox.x -2);
16960
16961                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16962                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16963                     cg.on('click', _this.onEventClick, _this, ev);
16964
16965                     ev.els.push(cg);
16966                     
16967                 }
16968                 
16969             }
16970             
16971             
16972             if(c.more.length){
16973                 var  cfg = {
16974                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16975                     style : 'position: absolute',
16976                     unselectable : "on",
16977                     cn : [
16978                         {
16979                             cls: 'fc-event-inner',
16980                             cn : [
16981                                 {
16982                                   tag:'span',
16983                                   cls: 'fc-event-title',
16984                                   html : 'More'
16985                                 }
16986
16987
16988                             ]
16989                         },
16990                         {
16991                             cls: 'ui-resizable-handle ui-resizable-e',
16992                             html : '&nbsp;&nbsp;&nbsp'
16993                         }
16994
16995                     ]
16996                 };
16997
16998                 var ctr = _this.el.select('.fc-event-container',true).first();
16999                 var cg = ctr.createChild(cfg);
17000
17001                 var sbox = c.select('.fc-day-content',true).first().getBox();
17002                 var ebox = c.select('.fc-day-content',true).first().getBox();
17003                 //Roo.log(cg);
17004                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17005                 cg.setWidth(ebox.right - sbox.x -2);
17006
17007                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17008                 
17009             }
17010             
17011         });
17012         
17013         
17014         
17015     },
17016     
17017     onEventEnter: function (e, el,event,d) {
17018         this.fireEvent('evententer', this, el, event);
17019     },
17020     
17021     onEventLeave: function (e, el,event,d) {
17022         this.fireEvent('eventleave', this, el, event);
17023     },
17024     
17025     onEventClick: function (e, el,event,d) {
17026         this.fireEvent('eventclick', this, el, event);
17027     },
17028     
17029     onMonthChange: function () {
17030         this.store.load();
17031     },
17032     
17033     onMoreEventClick: function(e, el, more)
17034     {
17035         var _this = this;
17036         
17037         this.calpopover.placement = 'right';
17038         this.calpopover.setTitle('More');
17039         
17040         this.calpopover.setContent('');
17041         
17042         var ctr = this.calpopover.el.select('.popover-content', true).first();
17043         
17044         Roo.each(more, function(m){
17045             var cfg = {
17046                 cls : 'fc-event-hori fc-event-draggable',
17047                 html : m.title
17048             };
17049             var cg = ctr.createChild(cfg);
17050             
17051             cg.on('click', _this.onEventClick, _this, m);
17052         });
17053         
17054         this.calpopover.show(el);
17055         
17056         
17057     },
17058     
17059     onLoad: function () 
17060     {   
17061         this.calevents = [];
17062         var cal = this;
17063         
17064         if(this.store.getCount() > 0){
17065             this.store.data.each(function(d){
17066                cal.addItem({
17067                     id : d.data.id,
17068                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17069                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17070                     time : d.data.start_time,
17071                     title : d.data.title,
17072                     description : d.data.description,
17073                     venue : d.data.venue
17074                 });
17075             });
17076         }
17077         
17078         this.renderEvents();
17079         
17080         if(this.calevents.length && this.loadMask){
17081             this.maskEl.hide();
17082         }
17083     },
17084     
17085     onBeforeLoad: function()
17086     {
17087         this.clearEvents();
17088         if(this.loadMask){
17089             this.maskEl.show();
17090         }
17091     }
17092 });
17093
17094  
17095  /*
17096  * - LGPL
17097  *
17098  * element
17099  * 
17100  */
17101
17102 /**
17103  * @class Roo.bootstrap.Popover
17104  * @extends Roo.bootstrap.Component
17105  * Bootstrap Popover class
17106  * @cfg {String} html contents of the popover   (or false to use children..)
17107  * @cfg {String} title of popover (or false to hide)
17108  * @cfg {String} placement how it is placed
17109  * @cfg {String} trigger click || hover (or false to trigger manually)
17110  * @cfg {String} over what (parent or false to trigger manually.)
17111  * @cfg {Number} delay - delay before showing
17112  
17113  * @constructor
17114  * Create a new Popover
17115  * @param {Object} config The config object
17116  */
17117
17118 Roo.bootstrap.Popover = function(config){
17119     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17120     
17121     this.addEvents({
17122         // raw events
17123          /**
17124          * @event show
17125          * After the popover show
17126          * 
17127          * @param {Roo.bootstrap.Popover} this
17128          */
17129         "show" : true,
17130         /**
17131          * @event hide
17132          * After the popover hide
17133          * 
17134          * @param {Roo.bootstrap.Popover} this
17135          */
17136         "hide" : true
17137     });
17138 };
17139
17140 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17141     
17142     title: 'Fill in a title',
17143     html: false,
17144     
17145     placement : 'right',
17146     trigger : 'hover', // hover
17147     
17148     delay : 0,
17149     
17150     over: 'parent',
17151     
17152     can_build_overlaid : false,
17153     
17154     getChildContainer : function()
17155     {
17156         return this.el.select('.popover-content',true).first();
17157     },
17158     
17159     getAutoCreate : function(){
17160          
17161         var cfg = {
17162            cls : 'popover roo-dynamic',
17163            style: 'display:block',
17164            cn : [
17165                 {
17166                     cls : 'arrow'
17167                 },
17168                 {
17169                     cls : 'popover-inner',
17170                     cn : [
17171                         {
17172                             tag: 'h3',
17173                             cls: 'popover-title',
17174                             html : this.title
17175                         },
17176                         {
17177                             cls : 'popover-content',
17178                             html : this.html
17179                         }
17180                     ]
17181                     
17182                 }
17183            ]
17184         };
17185         
17186         return cfg;
17187     },
17188     setTitle: function(str)
17189     {
17190         this.title = str;
17191         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17192     },
17193     setContent: function(str)
17194     {
17195         this.html = str;
17196         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17197     },
17198     // as it get's added to the bottom of the page.
17199     onRender : function(ct, position)
17200     {
17201         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17202         if(!this.el){
17203             var cfg = Roo.apply({},  this.getAutoCreate());
17204             cfg.id = Roo.id();
17205             
17206             if (this.cls) {
17207                 cfg.cls += ' ' + this.cls;
17208             }
17209             if (this.style) {
17210                 cfg.style = this.style;
17211             }
17212             //Roo.log("adding to ");
17213             this.el = Roo.get(document.body).createChild(cfg, position);
17214 //            Roo.log(this.el);
17215         }
17216         this.initEvents();
17217     },
17218     
17219     initEvents : function()
17220     {
17221         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17222         this.el.enableDisplayMode('block');
17223         this.el.hide();
17224         if (this.over === false) {
17225             return; 
17226         }
17227         if (this.triggers === false) {
17228             return;
17229         }
17230         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17231         var triggers = this.trigger ? this.trigger.split(' ') : [];
17232         Roo.each(triggers, function(trigger) {
17233         
17234             if (trigger == 'click') {
17235                 on_el.on('click', this.toggle, this);
17236             } else if (trigger != 'manual') {
17237                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17238                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17239       
17240                 on_el.on(eventIn  ,this.enter, this);
17241                 on_el.on(eventOut, this.leave, this);
17242             }
17243         }, this);
17244         
17245     },
17246     
17247     
17248     // private
17249     timeout : null,
17250     hoverState : null,
17251     
17252     toggle : function () {
17253         this.hoverState == 'in' ? this.leave() : this.enter();
17254     },
17255     
17256     enter : function () {
17257         
17258         clearTimeout(this.timeout);
17259     
17260         this.hoverState = 'in';
17261     
17262         if (!this.delay || !this.delay.show) {
17263             this.show();
17264             return;
17265         }
17266         var _t = this;
17267         this.timeout = setTimeout(function () {
17268             if (_t.hoverState == 'in') {
17269                 _t.show();
17270             }
17271         }, this.delay.show)
17272     },
17273     
17274     leave : function() {
17275         clearTimeout(this.timeout);
17276     
17277         this.hoverState = 'out';
17278     
17279         if (!this.delay || !this.delay.hide) {
17280             this.hide();
17281             return;
17282         }
17283         var _t = this;
17284         this.timeout = setTimeout(function () {
17285             if (_t.hoverState == 'out') {
17286                 _t.hide();
17287             }
17288         }, this.delay.hide)
17289     },
17290     
17291     show : function (on_el)
17292     {
17293         if (!on_el) {
17294             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17295         }
17296         
17297         // set content.
17298         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17299         if (this.html !== false) {
17300             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17301         }
17302         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17303         if (!this.title.length) {
17304             this.el.select('.popover-title',true).hide();
17305         }
17306         
17307         var placement = typeof this.placement == 'function' ?
17308             this.placement.call(this, this.el, on_el) :
17309             this.placement;
17310             
17311         var autoToken = /\s?auto?\s?/i;
17312         var autoPlace = autoToken.test(placement);
17313         if (autoPlace) {
17314             placement = placement.replace(autoToken, '') || 'top';
17315         }
17316         
17317         //this.el.detach()
17318         //this.el.setXY([0,0]);
17319         this.el.show();
17320         this.el.dom.style.display='block';
17321         this.el.addClass(placement);
17322         
17323         //this.el.appendTo(on_el);
17324         
17325         var p = this.getPosition();
17326         var box = this.el.getBox();
17327         
17328         if (autoPlace) {
17329             // fixme..
17330         }
17331         var align = Roo.bootstrap.Popover.alignment[placement];
17332         this.el.alignTo(on_el, align[0],align[1]);
17333         //var arrow = this.el.select('.arrow',true).first();
17334         //arrow.set(align[2], 
17335         
17336         this.el.addClass('in');
17337         
17338         
17339         if (this.el.hasClass('fade')) {
17340             // fade it?
17341         }
17342         
17343         this.hoverState = 'in';
17344         
17345         this.fireEvent('show', this);
17346         
17347     },
17348     hide : function()
17349     {
17350         this.el.setXY([0,0]);
17351         this.el.removeClass('in');
17352         this.el.hide();
17353         this.hoverState = null;
17354         
17355         this.fireEvent('hide', this);
17356     }
17357     
17358 });
17359
17360 Roo.bootstrap.Popover.alignment = {
17361     'left' : ['r-l', [-10,0], 'right'],
17362     'right' : ['l-r', [10,0], 'left'],
17363     'bottom' : ['t-b', [0,10], 'top'],
17364     'top' : [ 'b-t', [0,-10], 'bottom']
17365 };
17366
17367  /*
17368  * - LGPL
17369  *
17370  * Progress
17371  * 
17372  */
17373
17374 /**
17375  * @class Roo.bootstrap.Progress
17376  * @extends Roo.bootstrap.Component
17377  * Bootstrap Progress class
17378  * @cfg {Boolean} striped striped of the progress bar
17379  * @cfg {Boolean} active animated of the progress bar
17380  * 
17381  * 
17382  * @constructor
17383  * Create a new Progress
17384  * @param {Object} config The config object
17385  */
17386
17387 Roo.bootstrap.Progress = function(config){
17388     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17389 };
17390
17391 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17392     
17393     striped : false,
17394     active: false,
17395     
17396     getAutoCreate : function(){
17397         var cfg = {
17398             tag: 'div',
17399             cls: 'progress'
17400         };
17401         
17402         
17403         if(this.striped){
17404             cfg.cls += ' progress-striped';
17405         }
17406       
17407         if(this.active){
17408             cfg.cls += ' active';
17409         }
17410         
17411         
17412         return cfg;
17413     }
17414    
17415 });
17416
17417  
17418
17419  /*
17420  * - LGPL
17421  *
17422  * ProgressBar
17423  * 
17424  */
17425
17426 /**
17427  * @class Roo.bootstrap.ProgressBar
17428  * @extends Roo.bootstrap.Component
17429  * Bootstrap ProgressBar class
17430  * @cfg {Number} aria_valuenow aria-value now
17431  * @cfg {Number} aria_valuemin aria-value min
17432  * @cfg {Number} aria_valuemax aria-value max
17433  * @cfg {String} label label for the progress bar
17434  * @cfg {String} panel (success | info | warning | danger )
17435  * @cfg {String} role role of the progress bar
17436  * @cfg {String} sr_only text
17437  * 
17438  * 
17439  * @constructor
17440  * Create a new ProgressBar
17441  * @param {Object} config The config object
17442  */
17443
17444 Roo.bootstrap.ProgressBar = function(config){
17445     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17446 };
17447
17448 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17449     
17450     aria_valuenow : 0,
17451     aria_valuemin : 0,
17452     aria_valuemax : 100,
17453     label : false,
17454     panel : false,
17455     role : false,
17456     sr_only: false,
17457     
17458     getAutoCreate : function()
17459     {
17460         
17461         var cfg = {
17462             tag: 'div',
17463             cls: 'progress-bar',
17464             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17465         };
17466         
17467         if(this.sr_only){
17468             cfg.cn = {
17469                 tag: 'span',
17470                 cls: 'sr-only',
17471                 html: this.sr_only
17472             }
17473         }
17474         
17475         if(this.role){
17476             cfg.role = this.role;
17477         }
17478         
17479         if(this.aria_valuenow){
17480             cfg['aria-valuenow'] = this.aria_valuenow;
17481         }
17482         
17483         if(this.aria_valuemin){
17484             cfg['aria-valuemin'] = this.aria_valuemin;
17485         }
17486         
17487         if(this.aria_valuemax){
17488             cfg['aria-valuemax'] = this.aria_valuemax;
17489         }
17490         
17491         if(this.label && !this.sr_only){
17492             cfg.html = this.label;
17493         }
17494         
17495         if(this.panel){
17496             cfg.cls += ' progress-bar-' + this.panel;
17497         }
17498         
17499         return cfg;
17500     },
17501     
17502     update : function(aria_valuenow)
17503     {
17504         this.aria_valuenow = aria_valuenow;
17505         
17506         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17507     }
17508    
17509 });
17510
17511  
17512
17513  /*
17514  * - LGPL
17515  *
17516  * column
17517  * 
17518  */
17519
17520 /**
17521  * @class Roo.bootstrap.TabGroup
17522  * @extends Roo.bootstrap.Column
17523  * Bootstrap Column class
17524  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17525  * @cfg {Boolean} carousel true to make the group behave like a carousel
17526  * @cfg {Boolean} bullets show bullets for the panels
17527  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17528  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17529  * @cfg {Boolean} showarrow (true|false) show arrow default true
17530  * 
17531  * @constructor
17532  * Create a new TabGroup
17533  * @param {Object} config The config object
17534  */
17535
17536 Roo.bootstrap.TabGroup = function(config){
17537     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17538     if (!this.navId) {
17539         this.navId = Roo.id();
17540     }
17541     this.tabs = [];
17542     Roo.bootstrap.TabGroup.register(this);
17543     
17544 };
17545
17546 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17547     
17548     carousel : false,
17549     transition : false,
17550     bullets : 0,
17551     timer : 0,
17552     autoslide : false,
17553     slideFn : false,
17554     slideOnTouch : false,
17555     showarrow : true,
17556     
17557     getAutoCreate : function()
17558     {
17559         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17560         
17561         cfg.cls += ' tab-content';
17562         
17563         if (this.carousel) {
17564             cfg.cls += ' carousel slide';
17565             
17566             cfg.cn = [{
17567                cls : 'carousel-inner',
17568                cn : []
17569             }];
17570         
17571             if(this.bullets  && !Roo.isTouch){
17572                 
17573                 var bullets = {
17574                     cls : 'carousel-bullets',
17575                     cn : []
17576                 };
17577                
17578                 if(this.bullets_cls){
17579                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17580                 }
17581                 
17582                 bullets.cn.push({
17583                     cls : 'clear'
17584                 });
17585                 
17586                 cfg.cn[0].cn.push(bullets);
17587             }
17588             
17589             if(this.showarrow){
17590                 cfg.cn[0].cn.push({
17591                     tag : 'div',
17592                     class : 'carousel-arrow',
17593                     cn : [
17594                         {
17595                             tag : 'div',
17596                             class : 'carousel-prev',
17597                             cn : [
17598                                 {
17599                                     tag : 'i',
17600                                     class : 'fa fa-chevron-left'
17601                                 }
17602                             ]
17603                         },
17604                         {
17605                             tag : 'div',
17606                             class : 'carousel-next',
17607                             cn : [
17608                                 {
17609                                     tag : 'i',
17610                                     class : 'fa fa-chevron-right'
17611                                 }
17612                             ]
17613                         }
17614                     ]
17615                 });
17616             }
17617             
17618         }
17619         
17620         return cfg;
17621     },
17622     
17623     initEvents:  function()
17624     {
17625 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17626 //            this.el.on("touchstart", this.onTouchStart, this);
17627 //        }
17628         
17629         if(this.autoslide){
17630             var _this = this;
17631             
17632             this.slideFn = window.setInterval(function() {
17633                 _this.showPanelNext();
17634             }, this.timer);
17635         }
17636         
17637         if(this.showarrow){
17638             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17639             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17640         }
17641         
17642         
17643     },
17644     
17645 //    onTouchStart : function(e, el, o)
17646 //    {
17647 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17648 //            return;
17649 //        }
17650 //        
17651 //        this.showPanelNext();
17652 //    },
17653     
17654     
17655     getChildContainer : function()
17656     {
17657         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17658     },
17659     
17660     /**
17661     * register a Navigation item
17662     * @param {Roo.bootstrap.NavItem} the navitem to add
17663     */
17664     register : function(item)
17665     {
17666         this.tabs.push( item);
17667         item.navId = this.navId; // not really needed..
17668         this.addBullet();
17669     
17670     },
17671     
17672     getActivePanel : function()
17673     {
17674         var r = false;
17675         Roo.each(this.tabs, function(t) {
17676             if (t.active) {
17677                 r = t;
17678                 return false;
17679             }
17680             return null;
17681         });
17682         return r;
17683         
17684     },
17685     getPanelByName : function(n)
17686     {
17687         var r = false;
17688         Roo.each(this.tabs, function(t) {
17689             if (t.tabId == n) {
17690                 r = t;
17691                 return false;
17692             }
17693             return null;
17694         });
17695         return r;
17696     },
17697     indexOfPanel : function(p)
17698     {
17699         var r = false;
17700         Roo.each(this.tabs, function(t,i) {
17701             if (t.tabId == p.tabId) {
17702                 r = i;
17703                 return false;
17704             }
17705             return null;
17706         });
17707         return r;
17708     },
17709     /**
17710      * show a specific panel
17711      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17712      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17713      */
17714     showPanel : function (pan)
17715     {
17716         if(this.transition || typeof(pan) == 'undefined'){
17717             Roo.log("waiting for the transitionend");
17718             return;
17719         }
17720         
17721         if (typeof(pan) == 'number') {
17722             pan = this.tabs[pan];
17723         }
17724         
17725         if (typeof(pan) == 'string') {
17726             pan = this.getPanelByName(pan);
17727         }
17728         
17729         var cur = this.getActivePanel();
17730         
17731         if(!pan || !cur){
17732             Roo.log('pan or acitve pan is undefined');
17733             return false;
17734         }
17735         
17736         if (pan.tabId == this.getActivePanel().tabId) {
17737             return true;
17738         }
17739         
17740         if (false === cur.fireEvent('beforedeactivate')) {
17741             return false;
17742         }
17743         
17744         if(this.bullets > 0 && !Roo.isTouch){
17745             this.setActiveBullet(this.indexOfPanel(pan));
17746         }
17747         
17748         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17749             
17750             this.transition = true;
17751             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17752             var lr = dir == 'next' ? 'left' : 'right';
17753             pan.el.addClass(dir); // or prev
17754             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17755             cur.el.addClass(lr); // or right
17756             pan.el.addClass(lr);
17757             
17758             var _this = this;
17759             cur.el.on('transitionend', function() {
17760                 Roo.log("trans end?");
17761                 
17762                 pan.el.removeClass([lr,dir]);
17763                 pan.setActive(true);
17764                 
17765                 cur.el.removeClass([lr]);
17766                 cur.setActive(false);
17767                 
17768                 _this.transition = false;
17769                 
17770             }, this, { single:  true } );
17771             
17772             return true;
17773         }
17774         
17775         cur.setActive(false);
17776         pan.setActive(true);
17777         
17778         return true;
17779         
17780     },
17781     showPanelNext : function()
17782     {
17783         var i = this.indexOfPanel(this.getActivePanel());
17784         
17785         if (i >= this.tabs.length - 1 && !this.autoslide) {
17786             return;
17787         }
17788         
17789         if (i >= this.tabs.length - 1 && this.autoslide) {
17790             i = -1;
17791         }
17792         
17793         this.showPanel(this.tabs[i+1]);
17794     },
17795     
17796     showPanelPrev : function()
17797     {
17798         var i = this.indexOfPanel(this.getActivePanel());
17799         
17800         if (i  < 1 && !this.autoslide) {
17801             return;
17802         }
17803         
17804         if (i < 1 && this.autoslide) {
17805             i = this.tabs.length;
17806         }
17807         
17808         this.showPanel(this.tabs[i-1]);
17809     },
17810     
17811     
17812     addBullet: function()
17813     {
17814         if(!this.bullets || Roo.isTouch){
17815             return;
17816         }
17817         var ctr = this.el.select('.carousel-bullets',true).first();
17818         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17819         var bullet = ctr.createChild({
17820             cls : 'bullet bullet-' + i
17821         },ctr.dom.lastChild);
17822         
17823         
17824         var _this = this;
17825         
17826         bullet.on('click', (function(e, el, o, ii, t){
17827
17828             e.preventDefault();
17829
17830             this.showPanel(ii);
17831
17832             if(this.autoslide && this.slideFn){
17833                 clearInterval(this.slideFn);
17834                 this.slideFn = window.setInterval(function() {
17835                     _this.showPanelNext();
17836                 }, this.timer);
17837             }
17838
17839         }).createDelegate(this, [i, bullet], true));
17840                 
17841         
17842     },
17843      
17844     setActiveBullet : function(i)
17845     {
17846         if(Roo.isTouch){
17847             return;
17848         }
17849         
17850         Roo.each(this.el.select('.bullet', true).elements, function(el){
17851             el.removeClass('selected');
17852         });
17853
17854         var bullet = this.el.select('.bullet-' + i, true).first();
17855         
17856         if(!bullet){
17857             return;
17858         }
17859         
17860         bullet.addClass('selected');
17861     }
17862     
17863     
17864   
17865 });
17866
17867  
17868
17869  
17870  
17871 Roo.apply(Roo.bootstrap.TabGroup, {
17872     
17873     groups: {},
17874      /**
17875     * register a Navigation Group
17876     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17877     */
17878     register : function(navgrp)
17879     {
17880         this.groups[navgrp.navId] = navgrp;
17881         
17882     },
17883     /**
17884     * fetch a Navigation Group based on the navigation ID
17885     * if one does not exist , it will get created.
17886     * @param {string} the navgroup to add
17887     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17888     */
17889     get: function(navId) {
17890         if (typeof(this.groups[navId]) == 'undefined') {
17891             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17892         }
17893         return this.groups[navId] ;
17894     }
17895     
17896     
17897     
17898 });
17899
17900  /*
17901  * - LGPL
17902  *
17903  * TabPanel
17904  * 
17905  */
17906
17907 /**
17908  * @class Roo.bootstrap.TabPanel
17909  * @extends Roo.bootstrap.Component
17910  * Bootstrap TabPanel class
17911  * @cfg {Boolean} active panel active
17912  * @cfg {String} html panel content
17913  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17914  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17915  * @cfg {String} href click to link..
17916  * 
17917  * 
17918  * @constructor
17919  * Create a new TabPanel
17920  * @param {Object} config The config object
17921  */
17922
17923 Roo.bootstrap.TabPanel = function(config){
17924     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17925     this.addEvents({
17926         /**
17927              * @event changed
17928              * Fires when the active status changes
17929              * @param {Roo.bootstrap.TabPanel} this
17930              * @param {Boolean} state the new state
17931             
17932          */
17933         'changed': true,
17934         /**
17935              * @event beforedeactivate
17936              * Fires before a tab is de-activated - can be used to do validation on a form.
17937              * @param {Roo.bootstrap.TabPanel} this
17938              * @return {Boolean} false if there is an error
17939             
17940          */
17941         'beforedeactivate': true
17942      });
17943     
17944     this.tabId = this.tabId || Roo.id();
17945   
17946 };
17947
17948 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17949     
17950     active: false,
17951     html: false,
17952     tabId: false,
17953     navId : false,
17954     href : '',
17955     
17956     getAutoCreate : function(){
17957         var cfg = {
17958             tag: 'div',
17959             // item is needed for carousel - not sure if it has any effect otherwise
17960             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17961             html: this.html || ''
17962         };
17963         
17964         if(this.active){
17965             cfg.cls += ' active';
17966         }
17967         
17968         if(this.tabId){
17969             cfg.tabId = this.tabId;
17970         }
17971         
17972         
17973         return cfg;
17974     },
17975     
17976     initEvents:  function()
17977     {
17978         var p = this.parent();
17979         
17980         this.navId = this.navId || p.navId;
17981         
17982         if (typeof(this.navId) != 'undefined') {
17983             // not really needed.. but just in case.. parent should be a NavGroup.
17984             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17985             
17986             tg.register(this);
17987             
17988             var i = tg.tabs.length - 1;
17989             
17990             if(this.active && tg.bullets > 0 && i < tg.bullets){
17991                 tg.setActiveBullet(i);
17992             }
17993         }
17994         
17995         this.el.on('click', this.onClick, this);
17996         
17997         if(Roo.isTouch){
17998             this.el.on("touchstart", this.onTouchStart, this);
17999             this.el.on("touchmove", this.onTouchMove, this);
18000             this.el.on("touchend", this.onTouchEnd, this);
18001         }
18002         
18003     },
18004     
18005     onRender : function(ct, position)
18006     {
18007         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18008     },
18009     
18010     setActive : function(state)
18011     {
18012         Roo.log("panel - set active " + this.tabId + "=" + state);
18013         
18014         this.active = state;
18015         if (!state) {
18016             this.el.removeClass('active');
18017             
18018         } else  if (!this.el.hasClass('active')) {
18019             this.el.addClass('active');
18020         }
18021         
18022         this.fireEvent('changed', this, state);
18023     },
18024     
18025     onClick : function(e)
18026     {
18027         e.preventDefault();
18028         
18029         if(!this.href.length){
18030             return;
18031         }
18032         
18033         window.location.href = this.href;
18034     },
18035     
18036     startX : 0,
18037     startY : 0,
18038     endX : 0,
18039     endY : 0,
18040     swiping : false,
18041     
18042     onTouchStart : function(e)
18043     {
18044         this.swiping = false;
18045         
18046         this.startX = e.browserEvent.touches[0].clientX;
18047         this.startY = e.browserEvent.touches[0].clientY;
18048     },
18049     
18050     onTouchMove : function(e)
18051     {
18052         this.swiping = true;
18053         
18054         this.endX = e.browserEvent.touches[0].clientX;
18055         this.endY = e.browserEvent.touches[0].clientY;
18056     },
18057     
18058     onTouchEnd : function(e)
18059     {
18060         if(!this.swiping){
18061             this.onClick(e);
18062             return;
18063         }
18064         
18065         var tabGroup = this.parent();
18066         
18067         if(this.endX > this.startX){ // swiping right
18068             tabGroup.showPanelPrev();
18069             return;
18070         }
18071         
18072         if(this.startX > this.endX){ // swiping left
18073             tabGroup.showPanelNext();
18074             return;
18075         }
18076     }
18077     
18078     
18079 });
18080  
18081
18082  
18083
18084  /*
18085  * - LGPL
18086  *
18087  * DateField
18088  * 
18089  */
18090
18091 /**
18092  * @class Roo.bootstrap.DateField
18093  * @extends Roo.bootstrap.Input
18094  * Bootstrap DateField class
18095  * @cfg {Number} weekStart default 0
18096  * @cfg {String} viewMode default empty, (months|years)
18097  * @cfg {String} minViewMode default empty, (months|years)
18098  * @cfg {Number} startDate default -Infinity
18099  * @cfg {Number} endDate default Infinity
18100  * @cfg {Boolean} todayHighlight default false
18101  * @cfg {Boolean} todayBtn default false
18102  * @cfg {Boolean} calendarWeeks default false
18103  * @cfg {Object} daysOfWeekDisabled default empty
18104  * @cfg {Boolean} singleMode default false (true | false)
18105  * 
18106  * @cfg {Boolean} keyboardNavigation default true
18107  * @cfg {String} language default en
18108  * 
18109  * @constructor
18110  * Create a new DateField
18111  * @param {Object} config The config object
18112  */
18113
18114 Roo.bootstrap.DateField = function(config){
18115     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18116      this.addEvents({
18117             /**
18118              * @event show
18119              * Fires when this field show.
18120              * @param {Roo.bootstrap.DateField} this
18121              * @param {Mixed} date The date value
18122              */
18123             show : true,
18124             /**
18125              * @event show
18126              * Fires when this field hide.
18127              * @param {Roo.bootstrap.DateField} this
18128              * @param {Mixed} date The date value
18129              */
18130             hide : true,
18131             /**
18132              * @event select
18133              * Fires when select a date.
18134              * @param {Roo.bootstrap.DateField} this
18135              * @param {Mixed} date The date value
18136              */
18137             select : true,
18138             /**
18139              * @event beforeselect
18140              * Fires when before select a date.
18141              * @param {Roo.bootstrap.DateField} this
18142              * @param {Mixed} date The date value
18143              */
18144             beforeselect : true
18145         });
18146 };
18147
18148 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18149     
18150     /**
18151      * @cfg {String} format
18152      * The default date format string which can be overriden for localization support.  The format must be
18153      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18154      */
18155     format : "m/d/y",
18156     /**
18157      * @cfg {String} altFormats
18158      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18159      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18160      */
18161     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18162     
18163     weekStart : 0,
18164     
18165     viewMode : '',
18166     
18167     minViewMode : '',
18168     
18169     todayHighlight : false,
18170     
18171     todayBtn: false,
18172     
18173     language: 'en',
18174     
18175     keyboardNavigation: true,
18176     
18177     calendarWeeks: false,
18178     
18179     startDate: -Infinity,
18180     
18181     endDate: Infinity,
18182     
18183     daysOfWeekDisabled: [],
18184     
18185     _events: [],
18186     
18187     singleMode : false,
18188     
18189     UTCDate: function()
18190     {
18191         return new Date(Date.UTC.apply(Date, arguments));
18192     },
18193     
18194     UTCToday: function()
18195     {
18196         var today = new Date();
18197         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18198     },
18199     
18200     getDate: function() {
18201             var d = this.getUTCDate();
18202             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18203     },
18204     
18205     getUTCDate: function() {
18206             return this.date;
18207     },
18208     
18209     setDate: function(d) {
18210             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18211     },
18212     
18213     setUTCDate: function(d) {
18214             this.date = d;
18215             this.setValue(this.formatDate(this.date));
18216     },
18217         
18218     onRender: function(ct, position)
18219     {
18220         
18221         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18222         
18223         this.language = this.language || 'en';
18224         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18225         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18226         
18227         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18228         this.format = this.format || 'm/d/y';
18229         this.isInline = false;
18230         this.isInput = true;
18231         this.component = this.el.select('.add-on', true).first() || false;
18232         this.component = (this.component && this.component.length === 0) ? false : this.component;
18233         this.hasInput = this.component && this.inputEl().length;
18234         
18235         if (typeof(this.minViewMode === 'string')) {
18236             switch (this.minViewMode) {
18237                 case 'months':
18238                     this.minViewMode = 1;
18239                     break;
18240                 case 'years':
18241                     this.minViewMode = 2;
18242                     break;
18243                 default:
18244                     this.minViewMode = 0;
18245                     break;
18246             }
18247         }
18248         
18249         if (typeof(this.viewMode === 'string')) {
18250             switch (this.viewMode) {
18251                 case 'months':
18252                     this.viewMode = 1;
18253                     break;
18254                 case 'years':
18255                     this.viewMode = 2;
18256                     break;
18257                 default:
18258                     this.viewMode = 0;
18259                     break;
18260             }
18261         }
18262                 
18263         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18264         
18265 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18266         
18267         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18268         
18269         this.picker().on('mousedown', this.onMousedown, this);
18270         this.picker().on('click', this.onClick, this);
18271         
18272         this.picker().addClass('datepicker-dropdown');
18273         
18274         this.startViewMode = this.viewMode;
18275         
18276         if(this.singleMode){
18277             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18278                 v.setVisibilityMode(Roo.Element.DISPLAY);
18279                 v.hide();
18280             });
18281             
18282             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18283                 v.setStyle('width', '189px');
18284             });
18285         }
18286         
18287         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18288             if(!this.calendarWeeks){
18289                 v.remove();
18290                 return;
18291             }
18292             
18293             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18294             v.attr('colspan', function(i, val){
18295                 return parseInt(val) + 1;
18296             });
18297         });
18298                         
18299         
18300         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18301         
18302         this.setStartDate(this.startDate);
18303         this.setEndDate(this.endDate);
18304         
18305         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18306         
18307         this.fillDow();
18308         this.fillMonths();
18309         this.update();
18310         this.showMode();
18311         
18312         if(this.isInline) {
18313             this.show();
18314         }
18315     },
18316     
18317     picker : function()
18318     {
18319         return this.pickerEl;
18320 //        return this.el.select('.datepicker', true).first();
18321     },
18322     
18323     fillDow: function()
18324     {
18325         var dowCnt = this.weekStart;
18326         
18327         var dow = {
18328             tag: 'tr',
18329             cn: [
18330                 
18331             ]
18332         };
18333         
18334         if(this.calendarWeeks){
18335             dow.cn.push({
18336                 tag: 'th',
18337                 cls: 'cw',
18338                 html: '&nbsp;'
18339             })
18340         }
18341         
18342         while (dowCnt < this.weekStart + 7) {
18343             dow.cn.push({
18344                 tag: 'th',
18345                 cls: 'dow',
18346                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18347             });
18348         }
18349         
18350         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18351     },
18352     
18353     fillMonths: function()
18354     {    
18355         var i = 0;
18356         var months = this.picker().select('>.datepicker-months td', true).first();
18357         
18358         months.dom.innerHTML = '';
18359         
18360         while (i < 12) {
18361             var month = {
18362                 tag: 'span',
18363                 cls: 'month',
18364                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18365             };
18366             
18367             months.createChild(month);
18368         }
18369         
18370     },
18371     
18372     update: function()
18373     {
18374         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;
18375         
18376         if (this.date < this.startDate) {
18377             this.viewDate = new Date(this.startDate);
18378         } else if (this.date > this.endDate) {
18379             this.viewDate = new Date(this.endDate);
18380         } else {
18381             this.viewDate = new Date(this.date);
18382         }
18383         
18384         this.fill();
18385     },
18386     
18387     fill: function() 
18388     {
18389         var d = new Date(this.viewDate),
18390                 year = d.getUTCFullYear(),
18391                 month = d.getUTCMonth(),
18392                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18393                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18394                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18395                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18396                 currentDate = this.date && this.date.valueOf(),
18397                 today = this.UTCToday();
18398         
18399         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18400         
18401 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18402         
18403 //        this.picker.select('>tfoot th.today').
18404 //                                              .text(dates[this.language].today)
18405 //                                              .toggle(this.todayBtn !== false);
18406     
18407         this.updateNavArrows();
18408         this.fillMonths();
18409                                                 
18410         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18411         
18412         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18413          
18414         prevMonth.setUTCDate(day);
18415         
18416         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18417         
18418         var nextMonth = new Date(prevMonth);
18419         
18420         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18421         
18422         nextMonth = nextMonth.valueOf();
18423         
18424         var fillMonths = false;
18425         
18426         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18427         
18428         while(prevMonth.valueOf() < nextMonth) {
18429             var clsName = '';
18430             
18431             if (prevMonth.getUTCDay() === this.weekStart) {
18432                 if(fillMonths){
18433                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18434                 }
18435                     
18436                 fillMonths = {
18437                     tag: 'tr',
18438                     cn: []
18439                 };
18440                 
18441                 if(this.calendarWeeks){
18442                     // ISO 8601: First week contains first thursday.
18443                     // ISO also states week starts on Monday, but we can be more abstract here.
18444                     var
18445                     // Start of current week: based on weekstart/current date
18446                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18447                     // Thursday of this week
18448                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18449                     // First Thursday of year, year from thursday
18450                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18451                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18452                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18453                     
18454                     fillMonths.cn.push({
18455                         tag: 'td',
18456                         cls: 'cw',
18457                         html: calWeek
18458                     });
18459                 }
18460             }
18461             
18462             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18463                 clsName += ' old';
18464             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18465                 clsName += ' new';
18466             }
18467             if (this.todayHighlight &&
18468                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18469                 prevMonth.getUTCMonth() == today.getMonth() &&
18470                 prevMonth.getUTCDate() == today.getDate()) {
18471                 clsName += ' today';
18472             }
18473             
18474             if (currentDate && prevMonth.valueOf() === currentDate) {
18475                 clsName += ' active';
18476             }
18477             
18478             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18479                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18480                     clsName += ' disabled';
18481             }
18482             
18483             fillMonths.cn.push({
18484                 tag: 'td',
18485                 cls: 'day ' + clsName,
18486                 html: prevMonth.getDate()
18487             });
18488             
18489             prevMonth.setDate(prevMonth.getDate()+1);
18490         }
18491           
18492         var currentYear = this.date && this.date.getUTCFullYear();
18493         var currentMonth = this.date && this.date.getUTCMonth();
18494         
18495         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18496         
18497         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18498             v.removeClass('active');
18499             
18500             if(currentYear === year && k === currentMonth){
18501                 v.addClass('active');
18502             }
18503             
18504             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18505                 v.addClass('disabled');
18506             }
18507             
18508         });
18509         
18510         
18511         year = parseInt(year/10, 10) * 10;
18512         
18513         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18514         
18515         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18516         
18517         year -= 1;
18518         for (var i = -1; i < 11; i++) {
18519             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18520                 tag: 'span',
18521                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18522                 html: year
18523             });
18524             
18525             year += 1;
18526         }
18527     },
18528     
18529     showMode: function(dir) 
18530     {
18531         if (dir) {
18532             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18533         }
18534         
18535         Roo.each(this.picker().select('>div',true).elements, function(v){
18536             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18537             v.hide();
18538         });
18539         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18540     },
18541     
18542     place: function()
18543     {
18544         if(this.isInline) {
18545             return;
18546         }
18547         
18548         this.picker().removeClass(['bottom', 'top']);
18549         
18550         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18551             /*
18552              * place to the top of element!
18553              *
18554              */
18555             
18556             this.picker().addClass('top');
18557             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18558             
18559             return;
18560         }
18561         
18562         this.picker().addClass('bottom');
18563         
18564         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18565     },
18566     
18567     parseDate : function(value)
18568     {
18569         if(!value || value instanceof Date){
18570             return value;
18571         }
18572         var v = Date.parseDate(value, this.format);
18573         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18574             v = Date.parseDate(value, 'Y-m-d');
18575         }
18576         if(!v && this.altFormats){
18577             if(!this.altFormatsArray){
18578                 this.altFormatsArray = this.altFormats.split("|");
18579             }
18580             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18581                 v = Date.parseDate(value, this.altFormatsArray[i]);
18582             }
18583         }
18584         return v;
18585     },
18586     
18587     formatDate : function(date, fmt)
18588     {   
18589         return (!date || !(date instanceof Date)) ?
18590         date : date.dateFormat(fmt || this.format);
18591     },
18592     
18593     onFocus : function()
18594     {
18595         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18596         this.show();
18597     },
18598     
18599     onBlur : function()
18600     {
18601         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18602         
18603         var d = this.inputEl().getValue();
18604         
18605         this.setValue(d);
18606                 
18607         this.hide();
18608     },
18609     
18610     show : function()
18611     {
18612         this.picker().show();
18613         this.update();
18614         this.place();
18615         
18616         this.fireEvent('show', this, this.date);
18617     },
18618     
18619     hide : function()
18620     {
18621         if(this.isInline) {
18622             return;
18623         }
18624         this.picker().hide();
18625         this.viewMode = this.startViewMode;
18626         this.showMode();
18627         
18628         this.fireEvent('hide', this, this.date);
18629         
18630     },
18631     
18632     onMousedown: function(e)
18633     {
18634         e.stopPropagation();
18635         e.preventDefault();
18636     },
18637     
18638     keyup: function(e)
18639     {
18640         Roo.bootstrap.DateField.superclass.keyup.call(this);
18641         this.update();
18642     },
18643
18644     setValue: function(v)
18645     {
18646         if(this.fireEvent('beforeselect', this, v) !== false){
18647             var d = new Date(this.parseDate(v) ).clearTime();
18648         
18649             if(isNaN(d.getTime())){
18650                 this.date = this.viewDate = '';
18651                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18652                 return;
18653             }
18654
18655             v = this.formatDate(d);
18656
18657             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18658
18659             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18660
18661             this.update();
18662
18663             this.fireEvent('select', this, this.date);
18664         }
18665     },
18666     
18667     getValue: function()
18668     {
18669         return this.formatDate(this.date);
18670     },
18671     
18672     fireKey: function(e)
18673     {
18674         if (!this.picker().isVisible()){
18675             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18676                 this.show();
18677             }
18678             return;
18679         }
18680         
18681         var dateChanged = false,
18682         dir, day, month,
18683         newDate, newViewDate;
18684         
18685         switch(e.keyCode){
18686             case 27: // escape
18687                 this.hide();
18688                 e.preventDefault();
18689                 break;
18690             case 37: // left
18691             case 39: // right
18692                 if (!this.keyboardNavigation) {
18693                     break;
18694                 }
18695                 dir = e.keyCode == 37 ? -1 : 1;
18696                 
18697                 if (e.ctrlKey){
18698                     newDate = this.moveYear(this.date, dir);
18699                     newViewDate = this.moveYear(this.viewDate, dir);
18700                 } else if (e.shiftKey){
18701                     newDate = this.moveMonth(this.date, dir);
18702                     newViewDate = this.moveMonth(this.viewDate, dir);
18703                 } else {
18704                     newDate = new Date(this.date);
18705                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18706                     newViewDate = new Date(this.viewDate);
18707                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18708                 }
18709                 if (this.dateWithinRange(newDate)){
18710                     this.date = newDate;
18711                     this.viewDate = newViewDate;
18712                     this.setValue(this.formatDate(this.date));
18713 //                    this.update();
18714                     e.preventDefault();
18715                     dateChanged = true;
18716                 }
18717                 break;
18718             case 38: // up
18719             case 40: // down
18720                 if (!this.keyboardNavigation) {
18721                     break;
18722                 }
18723                 dir = e.keyCode == 38 ? -1 : 1;
18724                 if (e.ctrlKey){
18725                     newDate = this.moveYear(this.date, dir);
18726                     newViewDate = this.moveYear(this.viewDate, dir);
18727                 } else if (e.shiftKey){
18728                     newDate = this.moveMonth(this.date, dir);
18729                     newViewDate = this.moveMonth(this.viewDate, dir);
18730                 } else {
18731                     newDate = new Date(this.date);
18732                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18733                     newViewDate = new Date(this.viewDate);
18734                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18735                 }
18736                 if (this.dateWithinRange(newDate)){
18737                     this.date = newDate;
18738                     this.viewDate = newViewDate;
18739                     this.setValue(this.formatDate(this.date));
18740 //                    this.update();
18741                     e.preventDefault();
18742                     dateChanged = true;
18743                 }
18744                 break;
18745             case 13: // enter
18746                 this.setValue(this.formatDate(this.date));
18747                 this.hide();
18748                 e.preventDefault();
18749                 break;
18750             case 9: // tab
18751                 this.setValue(this.formatDate(this.date));
18752                 this.hide();
18753                 break;
18754             case 16: // shift
18755             case 17: // ctrl
18756             case 18: // alt
18757                 break;
18758             default :
18759                 this.hide();
18760                 
18761         }
18762     },
18763     
18764     
18765     onClick: function(e) 
18766     {
18767         e.stopPropagation();
18768         e.preventDefault();
18769         
18770         var target = e.getTarget();
18771         
18772         if(target.nodeName.toLowerCase() === 'i'){
18773             target = Roo.get(target).dom.parentNode;
18774         }
18775         
18776         var nodeName = target.nodeName;
18777         var className = target.className;
18778         var html = target.innerHTML;
18779         //Roo.log(nodeName);
18780         
18781         switch(nodeName.toLowerCase()) {
18782             case 'th':
18783                 switch(className) {
18784                     case 'switch':
18785                         this.showMode(1);
18786                         break;
18787                     case 'prev':
18788                     case 'next':
18789                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18790                         switch(this.viewMode){
18791                                 case 0:
18792                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18793                                         break;
18794                                 case 1:
18795                                 case 2:
18796                                         this.viewDate = this.moveYear(this.viewDate, dir);
18797                                         break;
18798                         }
18799                         this.fill();
18800                         break;
18801                     case 'today':
18802                         var date = new Date();
18803                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18804 //                        this.fill()
18805                         this.setValue(this.formatDate(this.date));
18806                         
18807                         this.hide();
18808                         break;
18809                 }
18810                 break;
18811             case 'span':
18812                 if (className.indexOf('disabled') < 0) {
18813                     this.viewDate.setUTCDate(1);
18814                     if (className.indexOf('month') > -1) {
18815                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18816                     } else {
18817                         var year = parseInt(html, 10) || 0;
18818                         this.viewDate.setUTCFullYear(year);
18819                         
18820                     }
18821                     
18822                     if(this.singleMode){
18823                         this.setValue(this.formatDate(this.viewDate));
18824                         this.hide();
18825                         return;
18826                     }
18827                     
18828                     this.showMode(-1);
18829                     this.fill();
18830                 }
18831                 break;
18832                 
18833             case 'td':
18834                 //Roo.log(className);
18835                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18836                     var day = parseInt(html, 10) || 1;
18837                     var year = this.viewDate.getUTCFullYear(),
18838                         month = this.viewDate.getUTCMonth();
18839
18840                     if (className.indexOf('old') > -1) {
18841                         if(month === 0 ){
18842                             month = 11;
18843                             year -= 1;
18844                         }else{
18845                             month -= 1;
18846                         }
18847                     } else if (className.indexOf('new') > -1) {
18848                         if (month == 11) {
18849                             month = 0;
18850                             year += 1;
18851                         } else {
18852                             month += 1;
18853                         }
18854                     }
18855                     //Roo.log([year,month,day]);
18856                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18857                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18858 //                    this.fill();
18859                     //Roo.log(this.formatDate(this.date));
18860                     this.setValue(this.formatDate(this.date));
18861                     this.hide();
18862                 }
18863                 break;
18864         }
18865     },
18866     
18867     setStartDate: function(startDate)
18868     {
18869         this.startDate = startDate || -Infinity;
18870         if (this.startDate !== -Infinity) {
18871             this.startDate = this.parseDate(this.startDate);
18872         }
18873         this.update();
18874         this.updateNavArrows();
18875     },
18876
18877     setEndDate: function(endDate)
18878     {
18879         this.endDate = endDate || Infinity;
18880         if (this.endDate !== Infinity) {
18881             this.endDate = this.parseDate(this.endDate);
18882         }
18883         this.update();
18884         this.updateNavArrows();
18885     },
18886     
18887     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18888     {
18889         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18890         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18891             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18892         }
18893         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18894             return parseInt(d, 10);
18895         });
18896         this.update();
18897         this.updateNavArrows();
18898     },
18899     
18900     updateNavArrows: function() 
18901     {
18902         if(this.singleMode){
18903             return;
18904         }
18905         
18906         var d = new Date(this.viewDate),
18907         year = d.getUTCFullYear(),
18908         month = d.getUTCMonth();
18909         
18910         Roo.each(this.picker().select('.prev', true).elements, function(v){
18911             v.show();
18912             switch (this.viewMode) {
18913                 case 0:
18914
18915                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18916                         v.hide();
18917                     }
18918                     break;
18919                 case 1:
18920                 case 2:
18921                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18922                         v.hide();
18923                     }
18924                     break;
18925             }
18926         });
18927         
18928         Roo.each(this.picker().select('.next', true).elements, function(v){
18929             v.show();
18930             switch (this.viewMode) {
18931                 case 0:
18932
18933                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18934                         v.hide();
18935                     }
18936                     break;
18937                 case 1:
18938                 case 2:
18939                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18940                         v.hide();
18941                     }
18942                     break;
18943             }
18944         })
18945     },
18946     
18947     moveMonth: function(date, dir)
18948     {
18949         if (!dir) {
18950             return date;
18951         }
18952         var new_date = new Date(date.valueOf()),
18953         day = new_date.getUTCDate(),
18954         month = new_date.getUTCMonth(),
18955         mag = Math.abs(dir),
18956         new_month, test;
18957         dir = dir > 0 ? 1 : -1;
18958         if (mag == 1){
18959             test = dir == -1
18960             // If going back one month, make sure month is not current month
18961             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18962             ? function(){
18963                 return new_date.getUTCMonth() == month;
18964             }
18965             // If going forward one month, make sure month is as expected
18966             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18967             : function(){
18968                 return new_date.getUTCMonth() != new_month;
18969             };
18970             new_month = month + dir;
18971             new_date.setUTCMonth(new_month);
18972             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18973             if (new_month < 0 || new_month > 11) {
18974                 new_month = (new_month + 12) % 12;
18975             }
18976         } else {
18977             // For magnitudes >1, move one month at a time...
18978             for (var i=0; i<mag; i++) {
18979                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18980                 new_date = this.moveMonth(new_date, dir);
18981             }
18982             // ...then reset the day, keeping it in the new month
18983             new_month = new_date.getUTCMonth();
18984             new_date.setUTCDate(day);
18985             test = function(){
18986                 return new_month != new_date.getUTCMonth();
18987             };
18988         }
18989         // Common date-resetting loop -- if date is beyond end of month, make it
18990         // end of month
18991         while (test()){
18992             new_date.setUTCDate(--day);
18993             new_date.setUTCMonth(new_month);
18994         }
18995         return new_date;
18996     },
18997
18998     moveYear: function(date, dir)
18999     {
19000         return this.moveMonth(date, dir*12);
19001     },
19002
19003     dateWithinRange: function(date)
19004     {
19005         return date >= this.startDate && date <= this.endDate;
19006     },
19007
19008     
19009     remove: function() 
19010     {
19011         this.picker().remove();
19012     },
19013     
19014     validateValue : function(value)
19015     {
19016         if(value.length < 1)  {
19017             if(this.allowBlank){
19018                 return true;
19019             }
19020             return false;
19021         }
19022         
19023         if(value.length < this.minLength){
19024             return false;
19025         }
19026         if(value.length > this.maxLength){
19027             return false;
19028         }
19029         if(this.vtype){
19030             var vt = Roo.form.VTypes;
19031             if(!vt[this.vtype](value, this)){
19032                 return false;
19033             }
19034         }
19035         if(typeof this.validator == "function"){
19036             var msg = this.validator(value);
19037             if(msg !== true){
19038                 return false;
19039             }
19040         }
19041         
19042         if(this.regex && !this.regex.test(value)){
19043             return false;
19044         }
19045         
19046         if(typeof(this.parseDate(value)) == 'undefined'){
19047             return false;
19048         }
19049         
19050         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19051             return false;
19052         }      
19053         
19054         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19055             return false;
19056         } 
19057         
19058         
19059         return true;
19060     }
19061    
19062 });
19063
19064 Roo.apply(Roo.bootstrap.DateField,  {
19065     
19066     head : {
19067         tag: 'thead',
19068         cn: [
19069         {
19070             tag: 'tr',
19071             cn: [
19072             {
19073                 tag: 'th',
19074                 cls: 'prev',
19075                 html: '<i class="fa fa-arrow-left"/>'
19076             },
19077             {
19078                 tag: 'th',
19079                 cls: 'switch',
19080                 colspan: '5'
19081             },
19082             {
19083                 tag: 'th',
19084                 cls: 'next',
19085                 html: '<i class="fa fa-arrow-right"/>'
19086             }
19087
19088             ]
19089         }
19090         ]
19091     },
19092     
19093     content : {
19094         tag: 'tbody',
19095         cn: [
19096         {
19097             tag: 'tr',
19098             cn: [
19099             {
19100                 tag: 'td',
19101                 colspan: '7'
19102             }
19103             ]
19104         }
19105         ]
19106     },
19107     
19108     footer : {
19109         tag: 'tfoot',
19110         cn: [
19111         {
19112             tag: 'tr',
19113             cn: [
19114             {
19115                 tag: 'th',
19116                 colspan: '7',
19117                 cls: 'today'
19118             }
19119                     
19120             ]
19121         }
19122         ]
19123     },
19124     
19125     dates:{
19126         en: {
19127             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19128             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19129             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19130             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19131             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19132             today: "Today"
19133         }
19134     },
19135     
19136     modes: [
19137     {
19138         clsName: 'days',
19139         navFnc: 'Month',
19140         navStep: 1
19141     },
19142     {
19143         clsName: 'months',
19144         navFnc: 'FullYear',
19145         navStep: 1
19146     },
19147     {
19148         clsName: 'years',
19149         navFnc: 'FullYear',
19150         navStep: 10
19151     }]
19152 });
19153
19154 Roo.apply(Roo.bootstrap.DateField,  {
19155   
19156     template : {
19157         tag: 'div',
19158         cls: 'datepicker dropdown-menu roo-dynamic',
19159         cn: [
19160         {
19161             tag: 'div',
19162             cls: 'datepicker-days',
19163             cn: [
19164             {
19165                 tag: 'table',
19166                 cls: 'table-condensed',
19167                 cn:[
19168                 Roo.bootstrap.DateField.head,
19169                 {
19170                     tag: 'tbody'
19171                 },
19172                 Roo.bootstrap.DateField.footer
19173                 ]
19174             }
19175             ]
19176         },
19177         {
19178             tag: 'div',
19179             cls: 'datepicker-months',
19180             cn: [
19181             {
19182                 tag: 'table',
19183                 cls: 'table-condensed',
19184                 cn:[
19185                 Roo.bootstrap.DateField.head,
19186                 Roo.bootstrap.DateField.content,
19187                 Roo.bootstrap.DateField.footer
19188                 ]
19189             }
19190             ]
19191         },
19192         {
19193             tag: 'div',
19194             cls: 'datepicker-years',
19195             cn: [
19196             {
19197                 tag: 'table',
19198                 cls: 'table-condensed',
19199                 cn:[
19200                 Roo.bootstrap.DateField.head,
19201                 Roo.bootstrap.DateField.content,
19202                 Roo.bootstrap.DateField.footer
19203                 ]
19204             }
19205             ]
19206         }
19207         ]
19208     }
19209 });
19210
19211  
19212
19213  /*
19214  * - LGPL
19215  *
19216  * TimeField
19217  * 
19218  */
19219
19220 /**
19221  * @class Roo.bootstrap.TimeField
19222  * @extends Roo.bootstrap.Input
19223  * Bootstrap DateField class
19224  * 
19225  * 
19226  * @constructor
19227  * Create a new TimeField
19228  * @param {Object} config The config object
19229  */
19230
19231 Roo.bootstrap.TimeField = function(config){
19232     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19233     this.addEvents({
19234             /**
19235              * @event show
19236              * Fires when this field show.
19237              * @param {Roo.bootstrap.DateField} thisthis
19238              * @param {Mixed} date The date value
19239              */
19240             show : true,
19241             /**
19242              * @event show
19243              * Fires when this field hide.
19244              * @param {Roo.bootstrap.DateField} this
19245              * @param {Mixed} date The date value
19246              */
19247             hide : true,
19248             /**
19249              * @event select
19250              * Fires when select a date.
19251              * @param {Roo.bootstrap.DateField} this
19252              * @param {Mixed} date The date value
19253              */
19254             select : true
19255         });
19256 };
19257
19258 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19259     
19260     /**
19261      * @cfg {String} format
19262      * The default time format string which can be overriden for localization support.  The format must be
19263      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19264      */
19265     format : "H:i",
19266        
19267     onRender: function(ct, position)
19268     {
19269         
19270         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19271                 
19272         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19273         
19274         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19275         
19276         this.pop = this.picker().select('>.datepicker-time',true).first();
19277         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19278         
19279         this.picker().on('mousedown', this.onMousedown, this);
19280         this.picker().on('click', this.onClick, this);
19281         
19282         this.picker().addClass('datepicker-dropdown');
19283     
19284         this.fillTime();
19285         this.update();
19286             
19287         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19288         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19289         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19290         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19291         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19292         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19293
19294     },
19295     
19296     fireKey: function(e){
19297         if (!this.picker().isVisible()){
19298             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19299                 this.show();
19300             }
19301             return;
19302         }
19303
19304         e.preventDefault();
19305         
19306         switch(e.keyCode){
19307             case 27: // escape
19308                 this.hide();
19309                 break;
19310             case 37: // left
19311             case 39: // right
19312                 this.onTogglePeriod();
19313                 break;
19314             case 38: // up
19315                 this.onIncrementMinutes();
19316                 break;
19317             case 40: // down
19318                 this.onDecrementMinutes();
19319                 break;
19320             case 13: // enter
19321             case 9: // tab
19322                 this.setTime();
19323                 break;
19324         }
19325     },
19326     
19327     onClick: function(e) {
19328         e.stopPropagation();
19329         e.preventDefault();
19330     },
19331     
19332     picker : function()
19333     {
19334         return this.el.select('.datepicker', true).first();
19335     },
19336     
19337     fillTime: function()
19338     {    
19339         var time = this.pop.select('tbody', true).first();
19340         
19341         time.dom.innerHTML = '';
19342         
19343         time.createChild({
19344             tag: 'tr',
19345             cn: [
19346                 {
19347                     tag: 'td',
19348                     cn: [
19349                         {
19350                             tag: 'a',
19351                             href: '#',
19352                             cls: 'btn',
19353                             cn: [
19354                                 {
19355                                     tag: 'span',
19356                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19357                                 }
19358                             ]
19359                         } 
19360                     ]
19361                 },
19362                 {
19363                     tag: 'td',
19364                     cls: 'separator'
19365                 },
19366                 {
19367                     tag: 'td',
19368                     cn: [
19369                         {
19370                             tag: 'a',
19371                             href: '#',
19372                             cls: 'btn',
19373                             cn: [
19374                                 {
19375                                     tag: 'span',
19376                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19377                                 }
19378                             ]
19379                         }
19380                     ]
19381                 },
19382                 {
19383                     tag: 'td',
19384                     cls: 'separator'
19385                 }
19386             ]
19387         });
19388         
19389         time.createChild({
19390             tag: 'tr',
19391             cn: [
19392                 {
19393                     tag: 'td',
19394                     cn: [
19395                         {
19396                             tag: 'span',
19397                             cls: 'timepicker-hour',
19398                             html: '00'
19399                         }  
19400                     ]
19401                 },
19402                 {
19403                     tag: 'td',
19404                     cls: 'separator',
19405                     html: ':'
19406                 },
19407                 {
19408                     tag: 'td',
19409                     cn: [
19410                         {
19411                             tag: 'span',
19412                             cls: 'timepicker-minute',
19413                             html: '00'
19414                         }  
19415                     ]
19416                 },
19417                 {
19418                     tag: 'td',
19419                     cls: 'separator'
19420                 },
19421                 {
19422                     tag: 'td',
19423                     cn: [
19424                         {
19425                             tag: 'button',
19426                             type: 'button',
19427                             cls: 'btn btn-primary period',
19428                             html: 'AM'
19429                             
19430                         }
19431                     ]
19432                 }
19433             ]
19434         });
19435         
19436         time.createChild({
19437             tag: 'tr',
19438             cn: [
19439                 {
19440                     tag: 'td',
19441                     cn: [
19442                         {
19443                             tag: 'a',
19444                             href: '#',
19445                             cls: 'btn',
19446                             cn: [
19447                                 {
19448                                     tag: 'span',
19449                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19450                                 }
19451                             ]
19452                         }
19453                     ]
19454                 },
19455                 {
19456                     tag: 'td',
19457                     cls: 'separator'
19458                 },
19459                 {
19460                     tag: 'td',
19461                     cn: [
19462                         {
19463                             tag: 'a',
19464                             href: '#',
19465                             cls: 'btn',
19466                             cn: [
19467                                 {
19468                                     tag: 'span',
19469                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19470                                 }
19471                             ]
19472                         }
19473                     ]
19474                 },
19475                 {
19476                     tag: 'td',
19477                     cls: 'separator'
19478                 }
19479             ]
19480         });
19481         
19482     },
19483     
19484     update: function()
19485     {
19486         
19487         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19488         
19489         this.fill();
19490     },
19491     
19492     fill: function() 
19493     {
19494         var hours = this.time.getHours();
19495         var minutes = this.time.getMinutes();
19496         var period = 'AM';
19497         
19498         if(hours > 11){
19499             period = 'PM';
19500         }
19501         
19502         if(hours == 0){
19503             hours = 12;
19504         }
19505         
19506         
19507         if(hours > 12){
19508             hours = hours - 12;
19509         }
19510         
19511         if(hours < 10){
19512             hours = '0' + hours;
19513         }
19514         
19515         if(minutes < 10){
19516             minutes = '0' + minutes;
19517         }
19518         
19519         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19520         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19521         this.pop.select('button', true).first().dom.innerHTML = period;
19522         
19523     },
19524     
19525     place: function()
19526     {   
19527         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19528         
19529         var cls = ['bottom'];
19530         
19531         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19532             cls.pop();
19533             cls.push('top');
19534         }
19535         
19536         cls.push('right');
19537         
19538         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19539             cls.pop();
19540             cls.push('left');
19541         }
19542         
19543         this.picker().addClass(cls.join('-'));
19544         
19545         var _this = this;
19546         
19547         Roo.each(cls, function(c){
19548             if(c == 'bottom'){
19549                 _this.picker().setTop(_this.inputEl().getHeight());
19550                 return;
19551             }
19552             if(c == 'top'){
19553                 _this.picker().setTop(0 - _this.picker().getHeight());
19554                 return;
19555             }
19556             
19557             if(c == 'left'){
19558                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19559                 return;
19560             }
19561             if(c == 'right'){
19562                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19563                 return;
19564             }
19565         });
19566         
19567     },
19568   
19569     onFocus : function()
19570     {
19571         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19572         this.show();
19573     },
19574     
19575     onBlur : function()
19576     {
19577         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19578         this.hide();
19579     },
19580     
19581     show : function()
19582     {
19583         this.picker().show();
19584         this.pop.show();
19585         this.update();
19586         this.place();
19587         
19588         this.fireEvent('show', this, this.date);
19589     },
19590     
19591     hide : function()
19592     {
19593         this.picker().hide();
19594         this.pop.hide();
19595         
19596         this.fireEvent('hide', this, this.date);
19597     },
19598     
19599     setTime : function()
19600     {
19601         this.hide();
19602         this.setValue(this.time.format(this.format));
19603         
19604         this.fireEvent('select', this, this.date);
19605         
19606         
19607     },
19608     
19609     onMousedown: function(e){
19610         e.stopPropagation();
19611         e.preventDefault();
19612     },
19613     
19614     onIncrementHours: function()
19615     {
19616         Roo.log('onIncrementHours');
19617         this.time = this.time.add(Date.HOUR, 1);
19618         this.update();
19619         
19620     },
19621     
19622     onDecrementHours: function()
19623     {
19624         Roo.log('onDecrementHours');
19625         this.time = this.time.add(Date.HOUR, -1);
19626         this.update();
19627     },
19628     
19629     onIncrementMinutes: function()
19630     {
19631         Roo.log('onIncrementMinutes');
19632         this.time = this.time.add(Date.MINUTE, 1);
19633         this.update();
19634     },
19635     
19636     onDecrementMinutes: function()
19637     {
19638         Roo.log('onDecrementMinutes');
19639         this.time = this.time.add(Date.MINUTE, -1);
19640         this.update();
19641     },
19642     
19643     onTogglePeriod: function()
19644     {
19645         Roo.log('onTogglePeriod');
19646         this.time = this.time.add(Date.HOUR, 12);
19647         this.update();
19648     }
19649     
19650    
19651 });
19652
19653 Roo.apply(Roo.bootstrap.TimeField,  {
19654     
19655     content : {
19656         tag: 'tbody',
19657         cn: [
19658             {
19659                 tag: 'tr',
19660                 cn: [
19661                 {
19662                     tag: 'td',
19663                     colspan: '7'
19664                 }
19665                 ]
19666             }
19667         ]
19668     },
19669     
19670     footer : {
19671         tag: 'tfoot',
19672         cn: [
19673             {
19674                 tag: 'tr',
19675                 cn: [
19676                 {
19677                     tag: 'th',
19678                     colspan: '7',
19679                     cls: '',
19680                     cn: [
19681                         {
19682                             tag: 'button',
19683                             cls: 'btn btn-info ok',
19684                             html: 'OK'
19685                         }
19686                     ]
19687                 }
19688
19689                 ]
19690             }
19691         ]
19692     }
19693 });
19694
19695 Roo.apply(Roo.bootstrap.TimeField,  {
19696   
19697     template : {
19698         tag: 'div',
19699         cls: 'datepicker dropdown-menu',
19700         cn: [
19701             {
19702                 tag: 'div',
19703                 cls: 'datepicker-time',
19704                 cn: [
19705                 {
19706                     tag: 'table',
19707                     cls: 'table-condensed',
19708                     cn:[
19709                     Roo.bootstrap.TimeField.content,
19710                     Roo.bootstrap.TimeField.footer
19711                     ]
19712                 }
19713                 ]
19714             }
19715         ]
19716     }
19717 });
19718
19719  
19720
19721  /*
19722  * - LGPL
19723  *
19724  * MonthField
19725  * 
19726  */
19727
19728 /**
19729  * @class Roo.bootstrap.MonthField
19730  * @extends Roo.bootstrap.Input
19731  * Bootstrap MonthField class
19732  * 
19733  * @cfg {String} language default en
19734  * 
19735  * @constructor
19736  * Create a new MonthField
19737  * @param {Object} config The config object
19738  */
19739
19740 Roo.bootstrap.MonthField = function(config){
19741     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19742     
19743     this.addEvents({
19744         /**
19745          * @event show
19746          * Fires when this field show.
19747          * @param {Roo.bootstrap.MonthField} this
19748          * @param {Mixed} date The date value
19749          */
19750         show : true,
19751         /**
19752          * @event show
19753          * Fires when this field hide.
19754          * @param {Roo.bootstrap.MonthField} this
19755          * @param {Mixed} date The date value
19756          */
19757         hide : true,
19758         /**
19759          * @event select
19760          * Fires when select a date.
19761          * @param {Roo.bootstrap.MonthField} this
19762          * @param {String} oldvalue The old value
19763          * @param {String} newvalue The new value
19764          */
19765         select : true
19766     });
19767 };
19768
19769 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19770     
19771     onRender: function(ct, position)
19772     {
19773         
19774         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19775         
19776         this.language = this.language || 'en';
19777         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19778         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19779         
19780         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19781         this.isInline = false;
19782         this.isInput = true;
19783         this.component = this.el.select('.add-on', true).first() || false;
19784         this.component = (this.component && this.component.length === 0) ? false : this.component;
19785         this.hasInput = this.component && this.inputEL().length;
19786         
19787         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19788         
19789         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19790         
19791         this.picker().on('mousedown', this.onMousedown, this);
19792         this.picker().on('click', this.onClick, this);
19793         
19794         this.picker().addClass('datepicker-dropdown');
19795         
19796         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19797             v.setStyle('width', '189px');
19798         });
19799         
19800         this.fillMonths();
19801         
19802         this.update();
19803         
19804         if(this.isInline) {
19805             this.show();
19806         }
19807         
19808     },
19809     
19810     setValue: function(v, suppressEvent)
19811     {   
19812         var o = this.getValue();
19813         
19814         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19815         
19816         this.update();
19817
19818         if(suppressEvent !== true){
19819             this.fireEvent('select', this, o, v);
19820         }
19821         
19822     },
19823     
19824     getValue: function()
19825     {
19826         return this.value;
19827     },
19828     
19829     onClick: function(e) 
19830     {
19831         e.stopPropagation();
19832         e.preventDefault();
19833         
19834         var target = e.getTarget();
19835         
19836         if(target.nodeName.toLowerCase() === 'i'){
19837             target = Roo.get(target).dom.parentNode;
19838         }
19839         
19840         var nodeName = target.nodeName;
19841         var className = target.className;
19842         var html = target.innerHTML;
19843         
19844         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19845             return;
19846         }
19847         
19848         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19849         
19850         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19851         
19852         this.hide();
19853                         
19854     },
19855     
19856     picker : function()
19857     {
19858         return this.pickerEl;
19859     },
19860     
19861     fillMonths: function()
19862     {    
19863         var i = 0;
19864         var months = this.picker().select('>.datepicker-months td', true).first();
19865         
19866         months.dom.innerHTML = '';
19867         
19868         while (i < 12) {
19869             var month = {
19870                 tag: 'span',
19871                 cls: 'month',
19872                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19873             };
19874             
19875             months.createChild(month);
19876         }
19877         
19878     },
19879     
19880     update: function()
19881     {
19882         var _this = this;
19883         
19884         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19885             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19886         }
19887         
19888         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19889             e.removeClass('active');
19890             
19891             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19892                 e.addClass('active');
19893             }
19894         })
19895     },
19896     
19897     place: function()
19898     {
19899         if(this.isInline) {
19900             return;
19901         }
19902         
19903         this.picker().removeClass(['bottom', 'top']);
19904         
19905         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19906             /*
19907              * place to the top of element!
19908              *
19909              */
19910             
19911             this.picker().addClass('top');
19912             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19913             
19914             return;
19915         }
19916         
19917         this.picker().addClass('bottom');
19918         
19919         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19920     },
19921     
19922     onFocus : function()
19923     {
19924         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19925         this.show();
19926     },
19927     
19928     onBlur : function()
19929     {
19930         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19931         
19932         var d = this.inputEl().getValue();
19933         
19934         this.setValue(d);
19935                 
19936         this.hide();
19937     },
19938     
19939     show : function()
19940     {
19941         this.picker().show();
19942         this.picker().select('>.datepicker-months', true).first().show();
19943         this.update();
19944         this.place();
19945         
19946         this.fireEvent('show', this, this.date);
19947     },
19948     
19949     hide : function()
19950     {
19951         if(this.isInline) {
19952             return;
19953         }
19954         this.picker().hide();
19955         this.fireEvent('hide', this, this.date);
19956         
19957     },
19958     
19959     onMousedown: function(e)
19960     {
19961         e.stopPropagation();
19962         e.preventDefault();
19963     },
19964     
19965     keyup: function(e)
19966     {
19967         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19968         this.update();
19969     },
19970
19971     fireKey: function(e)
19972     {
19973         if (!this.picker().isVisible()){
19974             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19975                 this.show();
19976             }
19977             return;
19978         }
19979         
19980         var dir;
19981         
19982         switch(e.keyCode){
19983             case 27: // escape
19984                 this.hide();
19985                 e.preventDefault();
19986                 break;
19987             case 37: // left
19988             case 39: // right
19989                 dir = e.keyCode == 37 ? -1 : 1;
19990                 
19991                 this.vIndex = this.vIndex + dir;
19992                 
19993                 if(this.vIndex < 0){
19994                     this.vIndex = 0;
19995                 }
19996                 
19997                 if(this.vIndex > 11){
19998                     this.vIndex = 11;
19999                 }
20000                 
20001                 if(isNaN(this.vIndex)){
20002                     this.vIndex = 0;
20003                 }
20004                 
20005                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20006                 
20007                 break;
20008             case 38: // up
20009             case 40: // down
20010                 
20011                 dir = e.keyCode == 38 ? -1 : 1;
20012                 
20013                 this.vIndex = this.vIndex + dir * 4;
20014                 
20015                 if(this.vIndex < 0){
20016                     this.vIndex = 0;
20017                 }
20018                 
20019                 if(this.vIndex > 11){
20020                     this.vIndex = 11;
20021                 }
20022                 
20023                 if(isNaN(this.vIndex)){
20024                     this.vIndex = 0;
20025                 }
20026                 
20027                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20028                 break;
20029                 
20030             case 13: // enter
20031                 
20032                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20033                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20034                 }
20035                 
20036                 this.hide();
20037                 e.preventDefault();
20038                 break;
20039             case 9: // tab
20040                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20041                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20042                 }
20043                 this.hide();
20044                 break;
20045             case 16: // shift
20046             case 17: // ctrl
20047             case 18: // alt
20048                 break;
20049             default :
20050                 this.hide();
20051                 
20052         }
20053     },
20054     
20055     remove: function() 
20056     {
20057         this.picker().remove();
20058     }
20059    
20060 });
20061
20062 Roo.apply(Roo.bootstrap.MonthField,  {
20063     
20064     content : {
20065         tag: 'tbody',
20066         cn: [
20067         {
20068             tag: 'tr',
20069             cn: [
20070             {
20071                 tag: 'td',
20072                 colspan: '7'
20073             }
20074             ]
20075         }
20076         ]
20077     },
20078     
20079     dates:{
20080         en: {
20081             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20082             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20083         }
20084     }
20085 });
20086
20087 Roo.apply(Roo.bootstrap.MonthField,  {
20088   
20089     template : {
20090         tag: 'div',
20091         cls: 'datepicker dropdown-menu roo-dynamic',
20092         cn: [
20093             {
20094                 tag: 'div',
20095                 cls: 'datepicker-months',
20096                 cn: [
20097                 {
20098                     tag: 'table',
20099                     cls: 'table-condensed',
20100                     cn:[
20101                         Roo.bootstrap.DateField.content
20102                     ]
20103                 }
20104                 ]
20105             }
20106         ]
20107     }
20108 });
20109
20110  
20111
20112  
20113  /*
20114  * - LGPL
20115  *
20116  * CheckBox
20117  * 
20118  */
20119
20120 /**
20121  * @class Roo.bootstrap.CheckBox
20122  * @extends Roo.bootstrap.Input
20123  * Bootstrap CheckBox class
20124  * 
20125  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20126  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20127  * @cfg {String} boxLabel The text that appears beside the checkbox
20128  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20129  * @cfg {Boolean} checked initnal the element
20130  * @cfg {Boolean} inline inline the element (default false)
20131  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20132  * 
20133  * @constructor
20134  * Create a new CheckBox
20135  * @param {Object} config The config object
20136  */
20137
20138 Roo.bootstrap.CheckBox = function(config){
20139     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20140    
20141     this.addEvents({
20142         /**
20143         * @event check
20144         * Fires when the element is checked or unchecked.
20145         * @param {Roo.bootstrap.CheckBox} this This input
20146         * @param {Boolean} checked The new checked value
20147         */
20148        check : true
20149     });
20150     
20151 };
20152
20153 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20154   
20155     inputType: 'checkbox',
20156     inputValue: 1,
20157     valueOff: 0,
20158     boxLabel: false,
20159     checked: false,
20160     weight : false,
20161     inline: false,
20162     
20163     getAutoCreate : function()
20164     {
20165         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20166         
20167         var id = Roo.id();
20168         
20169         var cfg = {};
20170         
20171         cfg.cls = 'form-group ' + this.inputType; //input-group
20172         
20173         if(this.inline){
20174             cfg.cls += ' ' + this.inputType + '-inline';
20175         }
20176         
20177         var input =  {
20178             tag: 'input',
20179             id : id,
20180             type : this.inputType,
20181             value : this.inputValue,
20182             cls : 'roo-' + this.inputType, //'form-box',
20183             placeholder : this.placeholder || ''
20184             
20185         };
20186         
20187         if(this.inputType != 'radio'){
20188             var hidden =  {
20189                 tag: 'input',
20190                 type : 'hidden',
20191                 cls : 'roo-hidden-value',
20192                 value : this.checked ? this.valueOff : this.inputValue
20193             };
20194         }
20195         
20196             
20197         if (this.weight) { // Validity check?
20198             cfg.cls += " " + this.inputType + "-" + this.weight;
20199         }
20200         
20201         if (this.disabled) {
20202             input.disabled=true;
20203         }
20204         
20205         if(this.checked){
20206             input.checked = this.checked;
20207             
20208         }
20209         
20210         
20211         if (this.name) {
20212             
20213             input.name = this.name;
20214             
20215             if(this.inputType != 'radio'){
20216                 hidden.name = this.name;
20217                 input.name = '_hidden_' + this.name;
20218             }
20219         }
20220         
20221         if (this.size) {
20222             input.cls += ' input-' + this.size;
20223         }
20224         
20225         var settings=this;
20226         
20227         ['xs','sm','md','lg'].map(function(size){
20228             if (settings[size]) {
20229                 cfg.cls += ' col-' + size + '-' + settings[size];
20230             }
20231         });
20232         
20233         var inputblock = input;
20234          
20235         if (this.before || this.after) {
20236             
20237             inputblock = {
20238                 cls : 'input-group',
20239                 cn :  [] 
20240             };
20241             
20242             if (this.before) {
20243                 inputblock.cn.push({
20244                     tag :'span',
20245                     cls : 'input-group-addon',
20246                     html : this.before
20247                 });
20248             }
20249             
20250             inputblock.cn.push(input);
20251             
20252             if(this.inputType != 'radio'){
20253                 inputblock.cn.push(hidden);
20254             }
20255             
20256             if (this.after) {
20257                 inputblock.cn.push({
20258                     tag :'span',
20259                     cls : 'input-group-addon',
20260                     html : this.after
20261                 });
20262             }
20263             
20264         }
20265         
20266         if (align ==='left' && this.fieldLabel.length) {
20267 //                Roo.log("left and has label");
20268             cfg.cn = [
20269                 {
20270                     tag: 'label',
20271                     'for' :  id,
20272                     cls : 'control-label',
20273                     html : this.fieldLabel
20274
20275                 },
20276                 {
20277                     cls : "", 
20278                     cn: [
20279                         inputblock
20280                     ]
20281                 }
20282             ];
20283             
20284             if(this.labelWidth > 12){
20285                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20286             }
20287             
20288             if(this.labelWidth < 13 && this.labelmd == 0){
20289                 this.labelmd = this.labelWidth;
20290             }
20291             
20292             if(this.labellg > 0){
20293                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20294                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20295             }
20296             
20297             if(this.labelmd > 0){
20298                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20299                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20300             }
20301             
20302             if(this.labelsm > 0){
20303                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20304                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20305             }
20306             
20307             if(this.labelxs > 0){
20308                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20309                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20310             }
20311             
20312         } else if ( this.fieldLabel.length) {
20313 //                Roo.log(" label");
20314                 cfg.cn = [
20315                    
20316                     {
20317                         tag: this.boxLabel ? 'span' : 'label',
20318                         'for': id,
20319                         cls: 'control-label box-input-label',
20320                         //cls : 'input-group-addon',
20321                         html : this.fieldLabel
20322                         
20323                     },
20324                     
20325                     inputblock
20326                     
20327                 ];
20328
20329         } else {
20330             
20331 //                Roo.log(" no label && no align");
20332                 cfg.cn = [  inputblock ] ;
20333                 
20334                 
20335         }
20336         
20337         if(this.boxLabel){
20338              var boxLabelCfg = {
20339                 tag: 'label',
20340                 //'for': id, // box label is handled by onclick - so no for...
20341                 cls: 'box-label',
20342                 html: this.boxLabel
20343             };
20344             
20345             if(this.tooltip){
20346                 boxLabelCfg.tooltip = this.tooltip;
20347             }
20348              
20349             cfg.cn.push(boxLabelCfg);
20350         }
20351         
20352         if(this.inputType != 'radio'){
20353             cfg.cn.push(hidden);
20354         }
20355         
20356         return cfg;
20357         
20358     },
20359     
20360     /**
20361      * return the real input element.
20362      */
20363     inputEl: function ()
20364     {
20365         return this.el.select('input.roo-' + this.inputType,true).first();
20366     },
20367     hiddenEl: function ()
20368     {
20369         return this.el.select('input.roo-hidden-value',true).first();
20370     },
20371     
20372     labelEl: function()
20373     {
20374         return this.el.select('label.control-label',true).first();
20375     },
20376     /* depricated... */
20377     
20378     label: function()
20379     {
20380         return this.labelEl();
20381     },
20382     
20383     boxLabelEl: function()
20384     {
20385         return this.el.select('label.box-label',true).first();
20386     },
20387     
20388     initEvents : function()
20389     {
20390 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20391         
20392         this.inputEl().on('click', this.onClick,  this);
20393         
20394         if (this.boxLabel) { 
20395             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20396         }
20397         
20398         this.startValue = this.getValue();
20399         
20400         if(this.groupId){
20401             Roo.bootstrap.CheckBox.register(this);
20402         }
20403     },
20404     
20405     onClick : function()
20406     {   
20407         this.setChecked(!this.checked);
20408     },
20409     
20410     setChecked : function(state,suppressEvent)
20411     {
20412         this.startValue = this.getValue();
20413
20414         if(this.inputType == 'radio'){
20415             
20416             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20417                 e.dom.checked = false;
20418             });
20419             
20420             this.inputEl().dom.checked = true;
20421             
20422             this.inputEl().dom.value = this.inputValue;
20423             
20424             if(suppressEvent !== true){
20425                 this.fireEvent('check', this, true);
20426             }
20427             
20428             this.validate();
20429             
20430             return;
20431         }
20432         
20433         this.checked = state;
20434         
20435         this.inputEl().dom.checked = state;
20436         
20437         
20438         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20439         
20440         if(suppressEvent !== true){
20441             this.fireEvent('check', this, state);
20442         }
20443         
20444         this.validate();
20445     },
20446     
20447     getValue : function()
20448     {
20449         if(this.inputType == 'radio'){
20450             return this.getGroupValue();
20451         }
20452         
20453         return this.hiddenEl().dom.value;
20454         
20455     },
20456     
20457     getGroupValue : function()
20458     {
20459         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20460             return '';
20461         }
20462         
20463         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20464     },
20465     
20466     setValue : function(v,suppressEvent)
20467     {
20468         if(this.inputType == 'radio'){
20469             this.setGroupValue(v, suppressEvent);
20470             return;
20471         }
20472         
20473         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20474         
20475         this.validate();
20476     },
20477     
20478     setGroupValue : function(v, suppressEvent)
20479     {
20480         this.startValue = this.getValue();
20481         
20482         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20483             e.dom.checked = false;
20484             
20485             if(e.dom.value == v){
20486                 e.dom.checked = true;
20487             }
20488         });
20489         
20490         if(suppressEvent !== true){
20491             this.fireEvent('check', this, true);
20492         }
20493
20494         this.validate();
20495         
20496         return;
20497     },
20498     
20499     validate : function()
20500     {
20501         if(
20502                 this.disabled || 
20503                 (this.inputType == 'radio' && this.validateRadio()) ||
20504                 (this.inputType == 'checkbox' && this.validateCheckbox())
20505         ){
20506             this.markValid();
20507             return true;
20508         }
20509         
20510         this.markInvalid();
20511         return false;
20512     },
20513     
20514     validateRadio : function()
20515     {
20516         if(this.allowBlank){
20517             return true;
20518         }
20519         
20520         var valid = false;
20521         
20522         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20523             if(!e.dom.checked){
20524                 return;
20525             }
20526             
20527             valid = true;
20528             
20529             return false;
20530         });
20531         
20532         return valid;
20533     },
20534     
20535     validateCheckbox : function()
20536     {
20537         if(!this.groupId){
20538             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20539             //return (this.getValue() == this.inputValue) ? true : false;
20540         }
20541         
20542         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20543         
20544         if(!group){
20545             return false;
20546         }
20547         
20548         var r = false;
20549         
20550         for(var i in group){
20551             if(r){
20552                 break;
20553             }
20554             
20555             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20556         }
20557         
20558         return r;
20559     },
20560     
20561     /**
20562      * Mark this field as valid
20563      */
20564     markValid : function()
20565     {
20566         var _this = this;
20567         
20568         this.fireEvent('valid', this);
20569         
20570         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20571         
20572         if(this.groupId){
20573             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20574         }
20575         
20576         if(label){
20577             label.markValid();
20578         }
20579
20580         if(this.inputType == 'radio'){
20581             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20582                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20583                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20584             });
20585             
20586             return;
20587         }
20588
20589         if(!this.groupId){
20590             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20591             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20592             return;
20593         }
20594         
20595         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20596         
20597         if(!group){
20598             return;
20599         }
20600         
20601         for(var i in group){
20602             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20603             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20604         }
20605     },
20606     
20607      /**
20608      * Mark this field as invalid
20609      * @param {String} msg The validation message
20610      */
20611     markInvalid : function(msg)
20612     {
20613         if(this.allowBlank){
20614             return;
20615         }
20616         
20617         var _this = this;
20618         
20619         this.fireEvent('invalid', this, msg);
20620         
20621         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20622         
20623         if(this.groupId){
20624             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20625         }
20626         
20627         if(label){
20628             label.markInvalid();
20629         }
20630             
20631         if(this.inputType == 'radio'){
20632             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20633                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20634                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20635             });
20636             
20637             return;
20638         }
20639         
20640         if(!this.groupId){
20641             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20642             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20643             return;
20644         }
20645         
20646         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20647         
20648         if(!group){
20649             return;
20650         }
20651         
20652         for(var i in group){
20653             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20654             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20655         }
20656         
20657     },
20658     
20659     clearInvalid : function()
20660     {
20661         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20662         
20663         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20664         
20665         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20666         
20667         if (label) {
20668             label.iconEl.removeClass(label.validClass);
20669             label.iconEl.removeClass(label.invalidClass);
20670         }
20671     },
20672     
20673     disable : function()
20674     {
20675         if(this.inputType != 'radio'){
20676             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20677             return;
20678         }
20679         
20680         var _this = this;
20681         
20682         if(this.rendered){
20683             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20684                 _this.getActionEl().addClass(this.disabledClass);
20685                 e.dom.disabled = true;
20686             });
20687         }
20688         
20689         this.disabled = true;
20690         this.fireEvent("disable", this);
20691         return this;
20692     },
20693
20694     enable : function()
20695     {
20696         if(this.inputType != 'radio'){
20697             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20698             return;
20699         }
20700         
20701         var _this = this;
20702         
20703         if(this.rendered){
20704             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20705                 _this.getActionEl().removeClass(this.disabledClass);
20706                 e.dom.disabled = false;
20707             });
20708         }
20709         
20710         this.disabled = false;
20711         this.fireEvent("enable", this);
20712         return this;
20713     }
20714
20715 });
20716
20717 Roo.apply(Roo.bootstrap.CheckBox, {
20718     
20719     groups: {},
20720     
20721      /**
20722     * register a CheckBox Group
20723     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20724     */
20725     register : function(checkbox)
20726     {
20727         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20728             this.groups[checkbox.groupId] = {};
20729         }
20730         
20731         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20732             return;
20733         }
20734         
20735         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20736         
20737     },
20738     /**
20739     * fetch a CheckBox Group based on the group ID
20740     * @param {string} the group ID
20741     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20742     */
20743     get: function(groupId) {
20744         if (typeof(this.groups[groupId]) == 'undefined') {
20745             return false;
20746         }
20747         
20748         return this.groups[groupId] ;
20749     }
20750     
20751     
20752 });
20753 /*
20754  * - LGPL
20755  *
20756  * RadioItem
20757  * 
20758  */
20759
20760 /**
20761  * @class Roo.bootstrap.Radio
20762  * @extends Roo.bootstrap.Component
20763  * Bootstrap Radio class
20764  * @cfg {String} boxLabel - the label associated
20765  * @cfg {String} value - the value of radio
20766  * 
20767  * @constructor
20768  * Create a new Radio
20769  * @param {Object} config The config object
20770  */
20771 Roo.bootstrap.Radio = function(config){
20772     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20773     
20774 };
20775
20776 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20777     
20778     boxLabel : '',
20779     
20780     value : '',
20781     
20782     getAutoCreate : function()
20783     {
20784         var cfg = {
20785             tag : 'div',
20786             cls : 'form-group radio',
20787             cn : [
20788                 {
20789                     tag : 'label',
20790                     cls : 'box-label',
20791                     html : this.boxLabel
20792                 }
20793             ]
20794         };
20795         
20796         return cfg;
20797     },
20798     
20799     initEvents : function() 
20800     {
20801         this.parent().register(this);
20802         
20803         this.el.on('click', this.onClick, this);
20804         
20805     },
20806     
20807     onClick : function()
20808     {
20809         this.setChecked(true);
20810     },
20811     
20812     setChecked : function(state, suppressEvent)
20813     {
20814         this.parent().setValue(this.value, suppressEvent);
20815         
20816     },
20817     
20818     setBoxLabel : function(v)
20819     {
20820         this.boxLabel = v;
20821         
20822         if(this.rendered){
20823             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20824         }
20825     }
20826     
20827 });
20828  
20829
20830  /*
20831  * - LGPL
20832  *
20833  * Input
20834  * 
20835  */
20836
20837 /**
20838  * @class Roo.bootstrap.SecurePass
20839  * @extends Roo.bootstrap.Input
20840  * Bootstrap SecurePass class
20841  *
20842  * 
20843  * @constructor
20844  * Create a new SecurePass
20845  * @param {Object} config The config object
20846  */
20847  
20848 Roo.bootstrap.SecurePass = function (config) {
20849     // these go here, so the translation tool can replace them..
20850     this.errors = {
20851         PwdEmpty: "Please type a password, and then retype it to confirm.",
20852         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20853         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20854         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20855         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20856         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20857         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20858         TooWeak: "Your password is Too Weak."
20859     },
20860     this.meterLabel = "Password strength:";
20861     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20862     this.meterClass = [
20863         "roo-password-meter-tooweak", 
20864         "roo-password-meter-weak", 
20865         "roo-password-meter-medium", 
20866         "roo-password-meter-strong", 
20867         "roo-password-meter-grey"
20868     ];
20869     
20870     this.errors = {};
20871     
20872     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20873 }
20874
20875 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20876     /**
20877      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20878      * {
20879      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20880      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20881      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20882      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20883      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20884      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20885      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20886      * })
20887      */
20888     // private
20889     
20890     meterWidth: 300,
20891     errorMsg :'',    
20892     errors: false,
20893     imageRoot: '/',
20894     /**
20895      * @cfg {String/Object} Label for the strength meter (defaults to
20896      * 'Password strength:')
20897      */
20898     // private
20899     meterLabel: '',
20900     /**
20901      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20902      * ['Weak', 'Medium', 'Strong'])
20903      */
20904     // private    
20905     pwdStrengths: false,    
20906     // private
20907     strength: 0,
20908     // private
20909     _lastPwd: null,
20910     // private
20911     kCapitalLetter: 0,
20912     kSmallLetter: 1,
20913     kDigit: 2,
20914     kPunctuation: 3,
20915     
20916     insecure: false,
20917     // private
20918     initEvents: function ()
20919     {
20920         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20921
20922         if (this.el.is('input[type=password]') && Roo.isSafari) {
20923             this.el.on('keydown', this.SafariOnKeyDown, this);
20924         }
20925
20926         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20927     },
20928     // private
20929     onRender: function (ct, position)
20930     {
20931         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20932         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20933         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20934
20935         this.trigger.createChild({
20936                    cn: [
20937                     {
20938                     //id: 'PwdMeter',
20939                     tag: 'div',
20940                     cls: 'roo-password-meter-grey col-xs-12',
20941                     style: {
20942                         //width: 0,
20943                         //width: this.meterWidth + 'px'                                                
20944                         }
20945                     },
20946                     {                            
20947                          cls: 'roo-password-meter-text'                          
20948                     }
20949                 ]            
20950         });
20951
20952          
20953         if (this.hideTrigger) {
20954             this.trigger.setDisplayed(false);
20955         }
20956         this.setSize(this.width || '', this.height || '');
20957     },
20958     // private
20959     onDestroy: function ()
20960     {
20961         if (this.trigger) {
20962             this.trigger.removeAllListeners();
20963             this.trigger.remove();
20964         }
20965         if (this.wrap) {
20966             this.wrap.remove();
20967         }
20968         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20969     },
20970     // private
20971     checkStrength: function ()
20972     {
20973         var pwd = this.inputEl().getValue();
20974         if (pwd == this._lastPwd) {
20975             return;
20976         }
20977
20978         var strength;
20979         if (this.ClientSideStrongPassword(pwd)) {
20980             strength = 3;
20981         } else if (this.ClientSideMediumPassword(pwd)) {
20982             strength = 2;
20983         } else if (this.ClientSideWeakPassword(pwd)) {
20984             strength = 1;
20985         } else {
20986             strength = 0;
20987         }
20988         
20989         Roo.log('strength1: ' + strength);
20990         
20991         //var pm = this.trigger.child('div/div/div').dom;
20992         var pm = this.trigger.child('div/div');
20993         pm.removeClass(this.meterClass);
20994         pm.addClass(this.meterClass[strength]);
20995                 
20996         
20997         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20998                 
20999         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21000         
21001         this._lastPwd = pwd;
21002     },
21003     reset: function ()
21004     {
21005         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21006         
21007         this._lastPwd = '';
21008         
21009         var pm = this.trigger.child('div/div');
21010         pm.removeClass(this.meterClass);
21011         pm.addClass('roo-password-meter-grey');        
21012         
21013         
21014         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21015         
21016         pt.innerHTML = '';
21017         this.inputEl().dom.type='password';
21018     },
21019     // private
21020     validateValue: function (value)
21021     {
21022         
21023         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21024             return false;
21025         }
21026         if (value.length == 0) {
21027             if (this.allowBlank) {
21028                 this.clearInvalid();
21029                 return true;
21030             }
21031
21032             this.markInvalid(this.errors.PwdEmpty);
21033             this.errorMsg = this.errors.PwdEmpty;
21034             return false;
21035         }
21036         
21037         if(this.insecure){
21038             return true;
21039         }
21040         
21041         if ('[\x21-\x7e]*'.match(value)) {
21042             this.markInvalid(this.errors.PwdBadChar);
21043             this.errorMsg = this.errors.PwdBadChar;
21044             return false;
21045         }
21046         if (value.length < 6) {
21047             this.markInvalid(this.errors.PwdShort);
21048             this.errorMsg = this.errors.PwdShort;
21049             return false;
21050         }
21051         if (value.length > 16) {
21052             this.markInvalid(this.errors.PwdLong);
21053             this.errorMsg = this.errors.PwdLong;
21054             return false;
21055         }
21056         var strength;
21057         if (this.ClientSideStrongPassword(value)) {
21058             strength = 3;
21059         } else if (this.ClientSideMediumPassword(value)) {
21060             strength = 2;
21061         } else if (this.ClientSideWeakPassword(value)) {
21062             strength = 1;
21063         } else {
21064             strength = 0;
21065         }
21066
21067         
21068         if (strength < 2) {
21069             //this.markInvalid(this.errors.TooWeak);
21070             this.errorMsg = this.errors.TooWeak;
21071             //return false;
21072         }
21073         
21074         
21075         console.log('strength2: ' + strength);
21076         
21077         //var pm = this.trigger.child('div/div/div').dom;
21078         
21079         var pm = this.trigger.child('div/div');
21080         pm.removeClass(this.meterClass);
21081         pm.addClass(this.meterClass[strength]);
21082                 
21083         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21084                 
21085         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21086         
21087         this.errorMsg = ''; 
21088         return true;
21089     },
21090     // private
21091     CharacterSetChecks: function (type)
21092     {
21093         this.type = type;
21094         this.fResult = false;
21095     },
21096     // private
21097     isctype: function (character, type)
21098     {
21099         switch (type) {  
21100             case this.kCapitalLetter:
21101                 if (character >= 'A' && character <= 'Z') {
21102                     return true;
21103                 }
21104                 break;
21105             
21106             case this.kSmallLetter:
21107                 if (character >= 'a' && character <= 'z') {
21108                     return true;
21109                 }
21110                 break;
21111             
21112             case this.kDigit:
21113                 if (character >= '0' && character <= '9') {
21114                     return true;
21115                 }
21116                 break;
21117             
21118             case this.kPunctuation:
21119                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21120                     return true;
21121                 }
21122                 break;
21123             
21124             default:
21125                 return false;
21126         }
21127
21128     },
21129     // private
21130     IsLongEnough: function (pwd, size)
21131     {
21132         return !(pwd == null || isNaN(size) || pwd.length < size);
21133     },
21134     // private
21135     SpansEnoughCharacterSets: function (word, nb)
21136     {
21137         if (!this.IsLongEnough(word, nb))
21138         {
21139             return false;
21140         }
21141
21142         var characterSetChecks = new Array(
21143             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21144             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21145         );
21146         
21147         for (var index = 0; index < word.length; ++index) {
21148             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21149                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21150                     characterSetChecks[nCharSet].fResult = true;
21151                     break;
21152                 }
21153             }
21154         }
21155
21156         var nCharSets = 0;
21157         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21158             if (characterSetChecks[nCharSet].fResult) {
21159                 ++nCharSets;
21160             }
21161         }
21162
21163         if (nCharSets < nb) {
21164             return false;
21165         }
21166         return true;
21167     },
21168     // private
21169     ClientSideStrongPassword: function (pwd)
21170     {
21171         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21172     },
21173     // private
21174     ClientSideMediumPassword: function (pwd)
21175     {
21176         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21177     },
21178     // private
21179     ClientSideWeakPassword: function (pwd)
21180     {
21181         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21182     }
21183           
21184 })//<script type="text/javascript">
21185
21186 /*
21187  * Based  Ext JS Library 1.1.1
21188  * Copyright(c) 2006-2007, Ext JS, LLC.
21189  * LGPL
21190  *
21191  */
21192  
21193 /**
21194  * @class Roo.HtmlEditorCore
21195  * @extends Roo.Component
21196  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21197  *
21198  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21199  */
21200
21201 Roo.HtmlEditorCore = function(config){
21202     
21203     
21204     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21205     
21206     
21207     this.addEvents({
21208         /**
21209          * @event initialize
21210          * Fires when the editor is fully initialized (including the iframe)
21211          * @param {Roo.HtmlEditorCore} this
21212          */
21213         initialize: true,
21214         /**
21215          * @event activate
21216          * Fires when the editor is first receives the focus. Any insertion must wait
21217          * until after this event.
21218          * @param {Roo.HtmlEditorCore} this
21219          */
21220         activate: true,
21221          /**
21222          * @event beforesync
21223          * Fires before the textarea is updated with content from the editor iframe. Return false
21224          * to cancel the sync.
21225          * @param {Roo.HtmlEditorCore} this
21226          * @param {String} html
21227          */
21228         beforesync: true,
21229          /**
21230          * @event beforepush
21231          * Fires before the iframe editor is updated with content from the textarea. Return false
21232          * to cancel the push.
21233          * @param {Roo.HtmlEditorCore} this
21234          * @param {String} html
21235          */
21236         beforepush: true,
21237          /**
21238          * @event sync
21239          * Fires when the textarea is updated with content from the editor iframe.
21240          * @param {Roo.HtmlEditorCore} this
21241          * @param {String} html
21242          */
21243         sync: true,
21244          /**
21245          * @event push
21246          * Fires when the iframe editor is updated with content from the textarea.
21247          * @param {Roo.HtmlEditorCore} this
21248          * @param {String} html
21249          */
21250         push: true,
21251         
21252         /**
21253          * @event editorevent
21254          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21255          * @param {Roo.HtmlEditorCore} this
21256          */
21257         editorevent: true
21258         
21259     });
21260     
21261     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21262     
21263     // defaults : white / black...
21264     this.applyBlacklists();
21265     
21266     
21267     
21268 };
21269
21270
21271 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21272
21273
21274      /**
21275      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21276      */
21277     
21278     owner : false,
21279     
21280      /**
21281      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21282      *                        Roo.resizable.
21283      */
21284     resizable : false,
21285      /**
21286      * @cfg {Number} height (in pixels)
21287      */   
21288     height: 300,
21289    /**
21290      * @cfg {Number} width (in pixels)
21291      */   
21292     width: 500,
21293     
21294     /**
21295      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21296      * 
21297      */
21298     stylesheets: false,
21299     
21300     // id of frame..
21301     frameId: false,
21302     
21303     // private properties
21304     validationEvent : false,
21305     deferHeight: true,
21306     initialized : false,
21307     activated : false,
21308     sourceEditMode : false,
21309     onFocus : Roo.emptyFn,
21310     iframePad:3,
21311     hideMode:'offsets',
21312     
21313     clearUp: true,
21314     
21315     // blacklist + whitelisted elements..
21316     black: false,
21317     white: false,
21318      
21319     bodyCls : '',
21320
21321     /**
21322      * Protected method that will not generally be called directly. It
21323      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21324      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21325      */
21326     getDocMarkup : function(){
21327         // body styles..
21328         var st = '';
21329         
21330         // inherit styels from page...?? 
21331         if (this.stylesheets === false) {
21332             
21333             Roo.get(document.head).select('style').each(function(node) {
21334                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21335             });
21336             
21337             Roo.get(document.head).select('link').each(function(node) { 
21338                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21339             });
21340             
21341         } else if (!this.stylesheets.length) {
21342                 // simple..
21343                 st = '<style type="text/css">' +
21344                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21345                    '</style>';
21346         } else { 
21347             st = '<style type="text/css">' +
21348                     this.stylesheets +
21349                 '</style>';
21350         }
21351         
21352         st +=  '<style type="text/css">' +
21353             'IMG { cursor: pointer } ' +
21354         '</style>';
21355
21356         var cls = 'roo-htmleditor-body';
21357         
21358         if(this.bodyCls.length){
21359             cls += ' ' + this.bodyCls;
21360         }
21361         
21362         return '<html><head>' + st  +
21363             //<style type="text/css">' +
21364             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21365             //'</style>' +
21366             ' </head><body class="' +  cls + '"></body></html>';
21367     },
21368
21369     // private
21370     onRender : function(ct, position)
21371     {
21372         var _t = this;
21373         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21374         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21375         
21376         
21377         this.el.dom.style.border = '0 none';
21378         this.el.dom.setAttribute('tabIndex', -1);
21379         this.el.addClass('x-hidden hide');
21380         
21381         
21382         
21383         if(Roo.isIE){ // fix IE 1px bogus margin
21384             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21385         }
21386        
21387         
21388         this.frameId = Roo.id();
21389         
21390          
21391         
21392         var iframe = this.owner.wrap.createChild({
21393             tag: 'iframe',
21394             cls: 'form-control', // bootstrap..
21395             id: this.frameId,
21396             name: this.frameId,
21397             frameBorder : 'no',
21398             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21399         }, this.el
21400         );
21401         
21402         
21403         this.iframe = iframe.dom;
21404
21405          this.assignDocWin();
21406         
21407         this.doc.designMode = 'on';
21408        
21409         this.doc.open();
21410         this.doc.write(this.getDocMarkup());
21411         this.doc.close();
21412
21413         
21414         var task = { // must defer to wait for browser to be ready
21415             run : function(){
21416                 //console.log("run task?" + this.doc.readyState);
21417                 this.assignDocWin();
21418                 if(this.doc.body || this.doc.readyState == 'complete'){
21419                     try {
21420                         this.doc.designMode="on";
21421                     } catch (e) {
21422                         return;
21423                     }
21424                     Roo.TaskMgr.stop(task);
21425                     this.initEditor.defer(10, this);
21426                 }
21427             },
21428             interval : 10,
21429             duration: 10000,
21430             scope: this
21431         };
21432         Roo.TaskMgr.start(task);
21433
21434     },
21435
21436     // private
21437     onResize : function(w, h)
21438     {
21439          Roo.log('resize: ' +w + ',' + h );
21440         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21441         if(!this.iframe){
21442             return;
21443         }
21444         if(typeof w == 'number'){
21445             
21446             this.iframe.style.width = w + 'px';
21447         }
21448         if(typeof h == 'number'){
21449             
21450             this.iframe.style.height = h + 'px';
21451             if(this.doc){
21452                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21453             }
21454         }
21455         
21456     },
21457
21458     /**
21459      * Toggles the editor between standard and source edit mode.
21460      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21461      */
21462     toggleSourceEdit : function(sourceEditMode){
21463         
21464         this.sourceEditMode = sourceEditMode === true;
21465         
21466         if(this.sourceEditMode){
21467  
21468             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21469             
21470         }else{
21471             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21472             //this.iframe.className = '';
21473             this.deferFocus();
21474         }
21475         //this.setSize(this.owner.wrap.getSize());
21476         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21477     },
21478
21479     
21480   
21481
21482     /**
21483      * Protected method that will not generally be called directly. If you need/want
21484      * custom HTML cleanup, this is the method you should override.
21485      * @param {String} html The HTML to be cleaned
21486      * return {String} The cleaned HTML
21487      */
21488     cleanHtml : function(html){
21489         html = String(html);
21490         if(html.length > 5){
21491             if(Roo.isSafari){ // strip safari nonsense
21492                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21493             }
21494         }
21495         if(html == '&nbsp;'){
21496             html = '';
21497         }
21498         return html;
21499     },
21500
21501     /**
21502      * HTML Editor -> Textarea
21503      * Protected method that will not generally be called directly. Syncs the contents
21504      * of the editor iframe with the textarea.
21505      */
21506     syncValue : function(){
21507         if(this.initialized){
21508             var bd = (this.doc.body || this.doc.documentElement);
21509             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21510             var html = bd.innerHTML;
21511             if(Roo.isSafari){
21512                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21513                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21514                 if(m && m[1]){
21515                     html = '<div style="'+m[0]+'">' + html + '</div>';
21516                 }
21517             }
21518             html = this.cleanHtml(html);
21519             // fix up the special chars.. normaly like back quotes in word...
21520             // however we do not want to do this with chinese..
21521             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21522                 var cc = b.charCodeAt();
21523                 if (
21524                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21525                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21526                     (cc >= 0xf900 && cc < 0xfb00 )
21527                 ) {
21528                         return b;
21529                 }
21530                 return "&#"+cc+";" 
21531             });
21532             if(this.owner.fireEvent('beforesync', this, html) !== false){
21533                 this.el.dom.value = html;
21534                 this.owner.fireEvent('sync', this, html);
21535             }
21536         }
21537     },
21538
21539     /**
21540      * Protected method that will not generally be called directly. Pushes the value of the textarea
21541      * into the iframe editor.
21542      */
21543     pushValue : function(){
21544         if(this.initialized){
21545             var v = this.el.dom.value.trim();
21546             
21547 //            if(v.length < 1){
21548 //                v = '&#160;';
21549 //            }
21550             
21551             if(this.owner.fireEvent('beforepush', this, v) !== false){
21552                 var d = (this.doc.body || this.doc.documentElement);
21553                 d.innerHTML = v;
21554                 this.cleanUpPaste();
21555                 this.el.dom.value = d.innerHTML;
21556                 this.owner.fireEvent('push', this, v);
21557             }
21558         }
21559     },
21560
21561     // private
21562     deferFocus : function(){
21563         this.focus.defer(10, this);
21564     },
21565
21566     // doc'ed in Field
21567     focus : function(){
21568         if(this.win && !this.sourceEditMode){
21569             this.win.focus();
21570         }else{
21571             this.el.focus();
21572         }
21573     },
21574     
21575     assignDocWin: function()
21576     {
21577         var iframe = this.iframe;
21578         
21579          if(Roo.isIE){
21580             this.doc = iframe.contentWindow.document;
21581             this.win = iframe.contentWindow;
21582         } else {
21583 //            if (!Roo.get(this.frameId)) {
21584 //                return;
21585 //            }
21586 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21587 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21588             
21589             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21590                 return;
21591             }
21592             
21593             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21594             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21595         }
21596     },
21597     
21598     // private
21599     initEditor : function(){
21600         //console.log("INIT EDITOR");
21601         this.assignDocWin();
21602         
21603         
21604         
21605         this.doc.designMode="on";
21606         this.doc.open();
21607         this.doc.write(this.getDocMarkup());
21608         this.doc.close();
21609         
21610         var dbody = (this.doc.body || this.doc.documentElement);
21611         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21612         // this copies styles from the containing element into thsi one..
21613         // not sure why we need all of this..
21614         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21615         
21616         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21617         //ss['background-attachment'] = 'fixed'; // w3c
21618         dbody.bgProperties = 'fixed'; // ie
21619         //Roo.DomHelper.applyStyles(dbody, ss);
21620         Roo.EventManager.on(this.doc, {
21621             //'mousedown': this.onEditorEvent,
21622             'mouseup': this.onEditorEvent,
21623             'dblclick': this.onEditorEvent,
21624             'click': this.onEditorEvent,
21625             'keyup': this.onEditorEvent,
21626             buffer:100,
21627             scope: this
21628         });
21629         if(Roo.isGecko){
21630             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21631         }
21632         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21633             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21634         }
21635         this.initialized = true;
21636
21637         this.owner.fireEvent('initialize', this);
21638         this.pushValue();
21639     },
21640
21641     // private
21642     onDestroy : function(){
21643         
21644         
21645         
21646         if(this.rendered){
21647             
21648             //for (var i =0; i < this.toolbars.length;i++) {
21649             //    // fixme - ask toolbars for heights?
21650             //    this.toolbars[i].onDestroy();
21651            // }
21652             
21653             //this.wrap.dom.innerHTML = '';
21654             //this.wrap.remove();
21655         }
21656     },
21657
21658     // private
21659     onFirstFocus : function(){
21660         
21661         this.assignDocWin();
21662         
21663         
21664         this.activated = true;
21665          
21666     
21667         if(Roo.isGecko){ // prevent silly gecko errors
21668             this.win.focus();
21669             var s = this.win.getSelection();
21670             if(!s.focusNode || s.focusNode.nodeType != 3){
21671                 var r = s.getRangeAt(0);
21672                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21673                 r.collapse(true);
21674                 this.deferFocus();
21675             }
21676             try{
21677                 this.execCmd('useCSS', true);
21678                 this.execCmd('styleWithCSS', false);
21679             }catch(e){}
21680         }
21681         this.owner.fireEvent('activate', this);
21682     },
21683
21684     // private
21685     adjustFont: function(btn){
21686         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21687         //if(Roo.isSafari){ // safari
21688         //    adjust *= 2;
21689        // }
21690         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21691         if(Roo.isSafari){ // safari
21692             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21693             v =  (v < 10) ? 10 : v;
21694             v =  (v > 48) ? 48 : v;
21695             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21696             
21697         }
21698         
21699         
21700         v = Math.max(1, v+adjust);
21701         
21702         this.execCmd('FontSize', v  );
21703     },
21704
21705     onEditorEvent : function(e)
21706     {
21707         this.owner.fireEvent('editorevent', this, e);
21708       //  this.updateToolbar();
21709         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21710     },
21711
21712     insertTag : function(tg)
21713     {
21714         // could be a bit smarter... -> wrap the current selected tRoo..
21715         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21716             
21717             range = this.createRange(this.getSelection());
21718             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21719             wrappingNode.appendChild(range.extractContents());
21720             range.insertNode(wrappingNode);
21721
21722             return;
21723             
21724             
21725             
21726         }
21727         this.execCmd("formatblock",   tg);
21728         
21729     },
21730     
21731     insertText : function(txt)
21732     {
21733         
21734         
21735         var range = this.createRange();
21736         range.deleteContents();
21737                //alert(Sender.getAttribute('label'));
21738                
21739         range.insertNode(this.doc.createTextNode(txt));
21740     } ,
21741     
21742      
21743
21744     /**
21745      * Executes a Midas editor command on the editor document and performs necessary focus and
21746      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21747      * @param {String} cmd The Midas command
21748      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21749      */
21750     relayCmd : function(cmd, value){
21751         this.win.focus();
21752         this.execCmd(cmd, value);
21753         this.owner.fireEvent('editorevent', this);
21754         //this.updateToolbar();
21755         this.owner.deferFocus();
21756     },
21757
21758     /**
21759      * Executes a Midas editor command directly on the editor document.
21760      * For visual commands, you should use {@link #relayCmd} instead.
21761      * <b>This should only be called after the editor is initialized.</b>
21762      * @param {String} cmd The Midas command
21763      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21764      */
21765     execCmd : function(cmd, value){
21766         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21767         this.syncValue();
21768     },
21769  
21770  
21771    
21772     /**
21773      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21774      * to insert tRoo.
21775      * @param {String} text | dom node.. 
21776      */
21777     insertAtCursor : function(text)
21778     {
21779         
21780         if(!this.activated){
21781             return;
21782         }
21783         /*
21784         if(Roo.isIE){
21785             this.win.focus();
21786             var r = this.doc.selection.createRange();
21787             if(r){
21788                 r.collapse(true);
21789                 r.pasteHTML(text);
21790                 this.syncValue();
21791                 this.deferFocus();
21792             
21793             }
21794             return;
21795         }
21796         */
21797         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21798             this.win.focus();
21799             
21800             
21801             // from jquery ui (MIT licenced)
21802             var range, node;
21803             var win = this.win;
21804             
21805             if (win.getSelection && win.getSelection().getRangeAt) {
21806                 range = win.getSelection().getRangeAt(0);
21807                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21808                 range.insertNode(node);
21809             } else if (win.document.selection && win.document.selection.createRange) {
21810                 // no firefox support
21811                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21812                 win.document.selection.createRange().pasteHTML(txt);
21813             } else {
21814                 // no firefox support
21815                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21816                 this.execCmd('InsertHTML', txt);
21817             } 
21818             
21819             this.syncValue();
21820             
21821             this.deferFocus();
21822         }
21823     },
21824  // private
21825     mozKeyPress : function(e){
21826         if(e.ctrlKey){
21827             var c = e.getCharCode(), cmd;
21828           
21829             if(c > 0){
21830                 c = String.fromCharCode(c).toLowerCase();
21831                 switch(c){
21832                     case 'b':
21833                         cmd = 'bold';
21834                         break;
21835                     case 'i':
21836                         cmd = 'italic';
21837                         break;
21838                     
21839                     case 'u':
21840                         cmd = 'underline';
21841                         break;
21842                     
21843                     case 'v':
21844                         this.cleanUpPaste.defer(100, this);
21845                         return;
21846                         
21847                 }
21848                 if(cmd){
21849                     this.win.focus();
21850                     this.execCmd(cmd);
21851                     this.deferFocus();
21852                     e.preventDefault();
21853                 }
21854                 
21855             }
21856         }
21857     },
21858
21859     // private
21860     fixKeys : function(){ // load time branching for fastest keydown performance
21861         if(Roo.isIE){
21862             return function(e){
21863                 var k = e.getKey(), r;
21864                 if(k == e.TAB){
21865                     e.stopEvent();
21866                     r = this.doc.selection.createRange();
21867                     if(r){
21868                         r.collapse(true);
21869                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21870                         this.deferFocus();
21871                     }
21872                     return;
21873                 }
21874                 
21875                 if(k == e.ENTER){
21876                     r = this.doc.selection.createRange();
21877                     if(r){
21878                         var target = r.parentElement();
21879                         if(!target || target.tagName.toLowerCase() != 'li'){
21880                             e.stopEvent();
21881                             r.pasteHTML('<br />');
21882                             r.collapse(false);
21883                             r.select();
21884                         }
21885                     }
21886                 }
21887                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21888                     this.cleanUpPaste.defer(100, this);
21889                     return;
21890                 }
21891                 
21892                 
21893             };
21894         }else if(Roo.isOpera){
21895             return function(e){
21896                 var k = e.getKey();
21897                 if(k == e.TAB){
21898                     e.stopEvent();
21899                     this.win.focus();
21900                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21901                     this.deferFocus();
21902                 }
21903                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21904                     this.cleanUpPaste.defer(100, this);
21905                     return;
21906                 }
21907                 
21908             };
21909         }else if(Roo.isSafari){
21910             return function(e){
21911                 var k = e.getKey();
21912                 
21913                 if(k == e.TAB){
21914                     e.stopEvent();
21915                     this.execCmd('InsertText','\t');
21916                     this.deferFocus();
21917                     return;
21918                 }
21919                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21920                     this.cleanUpPaste.defer(100, this);
21921                     return;
21922                 }
21923                 
21924              };
21925         }
21926     }(),
21927     
21928     getAllAncestors: function()
21929     {
21930         var p = this.getSelectedNode();
21931         var a = [];
21932         if (!p) {
21933             a.push(p); // push blank onto stack..
21934             p = this.getParentElement();
21935         }
21936         
21937         
21938         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21939             a.push(p);
21940             p = p.parentNode;
21941         }
21942         a.push(this.doc.body);
21943         return a;
21944     },
21945     lastSel : false,
21946     lastSelNode : false,
21947     
21948     
21949     getSelection : function() 
21950     {
21951         this.assignDocWin();
21952         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21953     },
21954     
21955     getSelectedNode: function() 
21956     {
21957         // this may only work on Gecko!!!
21958         
21959         // should we cache this!!!!
21960         
21961         
21962         
21963          
21964         var range = this.createRange(this.getSelection()).cloneRange();
21965         
21966         if (Roo.isIE) {
21967             var parent = range.parentElement();
21968             while (true) {
21969                 var testRange = range.duplicate();
21970                 testRange.moveToElementText(parent);
21971                 if (testRange.inRange(range)) {
21972                     break;
21973                 }
21974                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21975                     break;
21976                 }
21977                 parent = parent.parentElement;
21978             }
21979             return parent;
21980         }
21981         
21982         // is ancestor a text element.
21983         var ac =  range.commonAncestorContainer;
21984         if (ac.nodeType == 3) {
21985             ac = ac.parentNode;
21986         }
21987         
21988         var ar = ac.childNodes;
21989          
21990         var nodes = [];
21991         var other_nodes = [];
21992         var has_other_nodes = false;
21993         for (var i=0;i<ar.length;i++) {
21994             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21995                 continue;
21996             }
21997             // fullly contained node.
21998             
21999             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22000                 nodes.push(ar[i]);
22001                 continue;
22002             }
22003             
22004             // probably selected..
22005             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22006                 other_nodes.push(ar[i]);
22007                 continue;
22008             }
22009             // outer..
22010             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22011                 continue;
22012             }
22013             
22014             
22015             has_other_nodes = true;
22016         }
22017         if (!nodes.length && other_nodes.length) {
22018             nodes= other_nodes;
22019         }
22020         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22021             return false;
22022         }
22023         
22024         return nodes[0];
22025     },
22026     createRange: function(sel)
22027     {
22028         // this has strange effects when using with 
22029         // top toolbar - not sure if it's a great idea.
22030         //this.editor.contentWindow.focus();
22031         if (typeof sel != "undefined") {
22032             try {
22033                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22034             } catch(e) {
22035                 return this.doc.createRange();
22036             }
22037         } else {
22038             return this.doc.createRange();
22039         }
22040     },
22041     getParentElement: function()
22042     {
22043         
22044         this.assignDocWin();
22045         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22046         
22047         var range = this.createRange(sel);
22048          
22049         try {
22050             var p = range.commonAncestorContainer;
22051             while (p.nodeType == 3) { // text node
22052                 p = p.parentNode;
22053             }
22054             return p;
22055         } catch (e) {
22056             return null;
22057         }
22058     
22059     },
22060     /***
22061      *
22062      * Range intersection.. the hard stuff...
22063      *  '-1' = before
22064      *  '0' = hits..
22065      *  '1' = after.
22066      *         [ -- selected range --- ]
22067      *   [fail]                        [fail]
22068      *
22069      *    basically..
22070      *      if end is before start or  hits it. fail.
22071      *      if start is after end or hits it fail.
22072      *
22073      *   if either hits (but other is outside. - then it's not 
22074      *   
22075      *    
22076      **/
22077     
22078     
22079     // @see http://www.thismuchiknow.co.uk/?p=64.
22080     rangeIntersectsNode : function(range, node)
22081     {
22082         var nodeRange = node.ownerDocument.createRange();
22083         try {
22084             nodeRange.selectNode(node);
22085         } catch (e) {
22086             nodeRange.selectNodeContents(node);
22087         }
22088     
22089         var rangeStartRange = range.cloneRange();
22090         rangeStartRange.collapse(true);
22091     
22092         var rangeEndRange = range.cloneRange();
22093         rangeEndRange.collapse(false);
22094     
22095         var nodeStartRange = nodeRange.cloneRange();
22096         nodeStartRange.collapse(true);
22097     
22098         var nodeEndRange = nodeRange.cloneRange();
22099         nodeEndRange.collapse(false);
22100     
22101         return rangeStartRange.compareBoundaryPoints(
22102                  Range.START_TO_START, nodeEndRange) == -1 &&
22103                rangeEndRange.compareBoundaryPoints(
22104                  Range.START_TO_START, nodeStartRange) == 1;
22105         
22106          
22107     },
22108     rangeCompareNode : function(range, node)
22109     {
22110         var nodeRange = node.ownerDocument.createRange();
22111         try {
22112             nodeRange.selectNode(node);
22113         } catch (e) {
22114             nodeRange.selectNodeContents(node);
22115         }
22116         
22117         
22118         range.collapse(true);
22119     
22120         nodeRange.collapse(true);
22121      
22122         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22123         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22124          
22125         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22126         
22127         var nodeIsBefore   =  ss == 1;
22128         var nodeIsAfter    = ee == -1;
22129         
22130         if (nodeIsBefore && nodeIsAfter) {
22131             return 0; // outer
22132         }
22133         if (!nodeIsBefore && nodeIsAfter) {
22134             return 1; //right trailed.
22135         }
22136         
22137         if (nodeIsBefore && !nodeIsAfter) {
22138             return 2;  // left trailed.
22139         }
22140         // fully contined.
22141         return 3;
22142     },
22143
22144     // private? - in a new class?
22145     cleanUpPaste :  function()
22146     {
22147         // cleans up the whole document..
22148         Roo.log('cleanuppaste');
22149         
22150         this.cleanUpChildren(this.doc.body);
22151         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22152         if (clean != this.doc.body.innerHTML) {
22153             this.doc.body.innerHTML = clean;
22154         }
22155         
22156     },
22157     
22158     cleanWordChars : function(input) {// change the chars to hex code
22159         var he = Roo.HtmlEditorCore;
22160         
22161         var output = input;
22162         Roo.each(he.swapCodes, function(sw) { 
22163             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22164             
22165             output = output.replace(swapper, sw[1]);
22166         });
22167         
22168         return output;
22169     },
22170     
22171     
22172     cleanUpChildren : function (n)
22173     {
22174         if (!n.childNodes.length) {
22175             return;
22176         }
22177         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22178            this.cleanUpChild(n.childNodes[i]);
22179         }
22180     },
22181     
22182     
22183         
22184     
22185     cleanUpChild : function (node)
22186     {
22187         var ed = this;
22188         //console.log(node);
22189         if (node.nodeName == "#text") {
22190             // clean up silly Windows -- stuff?
22191             return; 
22192         }
22193         if (node.nodeName == "#comment") {
22194             node.parentNode.removeChild(node);
22195             // clean up silly Windows -- stuff?
22196             return; 
22197         }
22198         var lcname = node.tagName.toLowerCase();
22199         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22200         // whitelist of tags..
22201         
22202         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22203             // remove node.
22204             node.parentNode.removeChild(node);
22205             return;
22206             
22207         }
22208         
22209         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22210         
22211         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22212         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22213         
22214         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22215         //    remove_keep_children = true;
22216         //}
22217         
22218         if (remove_keep_children) {
22219             this.cleanUpChildren(node);
22220             // inserts everything just before this node...
22221             while (node.childNodes.length) {
22222                 var cn = node.childNodes[0];
22223                 node.removeChild(cn);
22224                 node.parentNode.insertBefore(cn, node);
22225             }
22226             node.parentNode.removeChild(node);
22227             return;
22228         }
22229         
22230         if (!node.attributes || !node.attributes.length) {
22231             this.cleanUpChildren(node);
22232             return;
22233         }
22234         
22235         function cleanAttr(n,v)
22236         {
22237             
22238             if (v.match(/^\./) || v.match(/^\//)) {
22239                 return;
22240             }
22241             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22242                 return;
22243             }
22244             if (v.match(/^#/)) {
22245                 return;
22246             }
22247 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22248             node.removeAttribute(n);
22249             
22250         }
22251         
22252         var cwhite = this.cwhite;
22253         var cblack = this.cblack;
22254             
22255         function cleanStyle(n,v)
22256         {
22257             if (v.match(/expression/)) { //XSS?? should we even bother..
22258                 node.removeAttribute(n);
22259                 return;
22260             }
22261             
22262             var parts = v.split(/;/);
22263             var clean = [];
22264             
22265             Roo.each(parts, function(p) {
22266                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22267                 if (!p.length) {
22268                     return true;
22269                 }
22270                 var l = p.split(':').shift().replace(/\s+/g,'');
22271                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22272                 
22273                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22274 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22275                     //node.removeAttribute(n);
22276                     return true;
22277                 }
22278                 //Roo.log()
22279                 // only allow 'c whitelisted system attributes'
22280                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22281 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22282                     //node.removeAttribute(n);
22283                     return true;
22284                 }
22285                 
22286                 
22287                  
22288                 
22289                 clean.push(p);
22290                 return true;
22291             });
22292             if (clean.length) { 
22293                 node.setAttribute(n, clean.join(';'));
22294             } else {
22295                 node.removeAttribute(n);
22296             }
22297             
22298         }
22299         
22300         
22301         for (var i = node.attributes.length-1; i > -1 ; i--) {
22302             var a = node.attributes[i];
22303             //console.log(a);
22304             
22305             if (a.name.toLowerCase().substr(0,2)=='on')  {
22306                 node.removeAttribute(a.name);
22307                 continue;
22308             }
22309             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22310                 node.removeAttribute(a.name);
22311                 continue;
22312             }
22313             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22314                 cleanAttr(a.name,a.value); // fixme..
22315                 continue;
22316             }
22317             if (a.name == 'style') {
22318                 cleanStyle(a.name,a.value);
22319                 continue;
22320             }
22321             /// clean up MS crap..
22322             // tecnically this should be a list of valid class'es..
22323             
22324             
22325             if (a.name == 'class') {
22326                 if (a.value.match(/^Mso/)) {
22327                     node.className = '';
22328                 }
22329                 
22330                 if (a.value.match(/^body$/)) {
22331                     node.className = '';
22332                 }
22333                 continue;
22334             }
22335             
22336             // style cleanup!?
22337             // class cleanup?
22338             
22339         }
22340         
22341         
22342         this.cleanUpChildren(node);
22343         
22344         
22345     },
22346     
22347     /**
22348      * Clean up MS wordisms...
22349      */
22350     cleanWord : function(node)
22351     {
22352         
22353         
22354         if (!node) {
22355             this.cleanWord(this.doc.body);
22356             return;
22357         }
22358         if (node.nodeName == "#text") {
22359             // clean up silly Windows -- stuff?
22360             return; 
22361         }
22362         if (node.nodeName == "#comment") {
22363             node.parentNode.removeChild(node);
22364             // clean up silly Windows -- stuff?
22365             return; 
22366         }
22367         
22368         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22369             node.parentNode.removeChild(node);
22370             return;
22371         }
22372         
22373         // remove - but keep children..
22374         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22375             while (node.childNodes.length) {
22376                 var cn = node.childNodes[0];
22377                 node.removeChild(cn);
22378                 node.parentNode.insertBefore(cn, node);
22379             }
22380             node.parentNode.removeChild(node);
22381             this.iterateChildren(node, this.cleanWord);
22382             return;
22383         }
22384         // clean styles
22385         if (node.className.length) {
22386             
22387             var cn = node.className.split(/\W+/);
22388             var cna = [];
22389             Roo.each(cn, function(cls) {
22390                 if (cls.match(/Mso[a-zA-Z]+/)) {
22391                     return;
22392                 }
22393                 cna.push(cls);
22394             });
22395             node.className = cna.length ? cna.join(' ') : '';
22396             if (!cna.length) {
22397                 node.removeAttribute("class");
22398             }
22399         }
22400         
22401         if (node.hasAttribute("lang")) {
22402             node.removeAttribute("lang");
22403         }
22404         
22405         if (node.hasAttribute("style")) {
22406             
22407             var styles = node.getAttribute("style").split(";");
22408             var nstyle = [];
22409             Roo.each(styles, function(s) {
22410                 if (!s.match(/:/)) {
22411                     return;
22412                 }
22413                 var kv = s.split(":");
22414                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22415                     return;
22416                 }
22417                 // what ever is left... we allow.
22418                 nstyle.push(s);
22419             });
22420             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22421             if (!nstyle.length) {
22422                 node.removeAttribute('style');
22423             }
22424         }
22425         this.iterateChildren(node, this.cleanWord);
22426         
22427         
22428         
22429     },
22430     /**
22431      * iterateChildren of a Node, calling fn each time, using this as the scole..
22432      * @param {DomNode} node node to iterate children of.
22433      * @param {Function} fn method of this class to call on each item.
22434      */
22435     iterateChildren : function(node, fn)
22436     {
22437         if (!node.childNodes.length) {
22438                 return;
22439         }
22440         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22441            fn.call(this, node.childNodes[i])
22442         }
22443     },
22444     
22445     
22446     /**
22447      * cleanTableWidths.
22448      *
22449      * Quite often pasting from word etc.. results in tables with column and widths.
22450      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22451      *
22452      */
22453     cleanTableWidths : function(node)
22454     {
22455          
22456          
22457         if (!node) {
22458             this.cleanTableWidths(this.doc.body);
22459             return;
22460         }
22461         
22462         // ignore list...
22463         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22464             return; 
22465         }
22466         Roo.log(node.tagName);
22467         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22468             this.iterateChildren(node, this.cleanTableWidths);
22469             return;
22470         }
22471         if (node.hasAttribute('width')) {
22472             node.removeAttribute('width');
22473         }
22474         
22475          
22476         if (node.hasAttribute("style")) {
22477             // pretty basic...
22478             
22479             var styles = node.getAttribute("style").split(";");
22480             var nstyle = [];
22481             Roo.each(styles, function(s) {
22482                 if (!s.match(/:/)) {
22483                     return;
22484                 }
22485                 var kv = s.split(":");
22486                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22487                     return;
22488                 }
22489                 // what ever is left... we allow.
22490                 nstyle.push(s);
22491             });
22492             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22493             if (!nstyle.length) {
22494                 node.removeAttribute('style');
22495             }
22496         }
22497         
22498         this.iterateChildren(node, this.cleanTableWidths);
22499         
22500         
22501     },
22502     
22503     
22504     
22505     
22506     domToHTML : function(currentElement, depth, nopadtext) {
22507         
22508         depth = depth || 0;
22509         nopadtext = nopadtext || false;
22510     
22511         if (!currentElement) {
22512             return this.domToHTML(this.doc.body);
22513         }
22514         
22515         //Roo.log(currentElement);
22516         var j;
22517         var allText = false;
22518         var nodeName = currentElement.nodeName;
22519         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22520         
22521         if  (nodeName == '#text') {
22522             
22523             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22524         }
22525         
22526         
22527         var ret = '';
22528         if (nodeName != 'BODY') {
22529              
22530             var i = 0;
22531             // Prints the node tagName, such as <A>, <IMG>, etc
22532             if (tagName) {
22533                 var attr = [];
22534                 for(i = 0; i < currentElement.attributes.length;i++) {
22535                     // quoting?
22536                     var aname = currentElement.attributes.item(i).name;
22537                     if (!currentElement.attributes.item(i).value.length) {
22538                         continue;
22539                     }
22540                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22541                 }
22542                 
22543                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22544             } 
22545             else {
22546                 
22547                 // eack
22548             }
22549         } else {
22550             tagName = false;
22551         }
22552         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22553             return ret;
22554         }
22555         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22556             nopadtext = true;
22557         }
22558         
22559         
22560         // Traverse the tree
22561         i = 0;
22562         var currentElementChild = currentElement.childNodes.item(i);
22563         var allText = true;
22564         var innerHTML  = '';
22565         lastnode = '';
22566         while (currentElementChild) {
22567             // Formatting code (indent the tree so it looks nice on the screen)
22568             var nopad = nopadtext;
22569             if (lastnode == 'SPAN') {
22570                 nopad  = true;
22571             }
22572             // text
22573             if  (currentElementChild.nodeName == '#text') {
22574                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22575                 toadd = nopadtext ? toadd : toadd.trim();
22576                 if (!nopad && toadd.length > 80) {
22577                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22578                 }
22579                 innerHTML  += toadd;
22580                 
22581                 i++;
22582                 currentElementChild = currentElement.childNodes.item(i);
22583                 lastNode = '';
22584                 continue;
22585             }
22586             allText = false;
22587             
22588             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22589                 
22590             // Recursively traverse the tree structure of the child node
22591             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22592             lastnode = currentElementChild.nodeName;
22593             i++;
22594             currentElementChild=currentElement.childNodes.item(i);
22595         }
22596         
22597         ret += innerHTML;
22598         
22599         if (!allText) {
22600                 // The remaining code is mostly for formatting the tree
22601             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22602         }
22603         
22604         
22605         if (tagName) {
22606             ret+= "</"+tagName+">";
22607         }
22608         return ret;
22609         
22610     },
22611         
22612     applyBlacklists : function()
22613     {
22614         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22615         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22616         
22617         this.white = [];
22618         this.black = [];
22619         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22620             if (b.indexOf(tag) > -1) {
22621                 return;
22622             }
22623             this.white.push(tag);
22624             
22625         }, this);
22626         
22627         Roo.each(w, function(tag) {
22628             if (b.indexOf(tag) > -1) {
22629                 return;
22630             }
22631             if (this.white.indexOf(tag) > -1) {
22632                 return;
22633             }
22634             this.white.push(tag);
22635             
22636         }, this);
22637         
22638         
22639         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22640             if (w.indexOf(tag) > -1) {
22641                 return;
22642             }
22643             this.black.push(tag);
22644             
22645         }, this);
22646         
22647         Roo.each(b, function(tag) {
22648             if (w.indexOf(tag) > -1) {
22649                 return;
22650             }
22651             if (this.black.indexOf(tag) > -1) {
22652                 return;
22653             }
22654             this.black.push(tag);
22655             
22656         }, this);
22657         
22658         
22659         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22660         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22661         
22662         this.cwhite = [];
22663         this.cblack = [];
22664         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22665             if (b.indexOf(tag) > -1) {
22666                 return;
22667             }
22668             this.cwhite.push(tag);
22669             
22670         }, this);
22671         
22672         Roo.each(w, function(tag) {
22673             if (b.indexOf(tag) > -1) {
22674                 return;
22675             }
22676             if (this.cwhite.indexOf(tag) > -1) {
22677                 return;
22678             }
22679             this.cwhite.push(tag);
22680             
22681         }, this);
22682         
22683         
22684         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22685             if (w.indexOf(tag) > -1) {
22686                 return;
22687             }
22688             this.cblack.push(tag);
22689             
22690         }, this);
22691         
22692         Roo.each(b, function(tag) {
22693             if (w.indexOf(tag) > -1) {
22694                 return;
22695             }
22696             if (this.cblack.indexOf(tag) > -1) {
22697                 return;
22698             }
22699             this.cblack.push(tag);
22700             
22701         }, this);
22702     },
22703     
22704     setStylesheets : function(stylesheets)
22705     {
22706         if(typeof(stylesheets) == 'string'){
22707             Roo.get(this.iframe.contentDocument.head).createChild({
22708                 tag : 'link',
22709                 rel : 'stylesheet',
22710                 type : 'text/css',
22711                 href : stylesheets
22712             });
22713             
22714             return;
22715         }
22716         var _this = this;
22717      
22718         Roo.each(stylesheets, function(s) {
22719             if(!s.length){
22720                 return;
22721             }
22722             
22723             Roo.get(_this.iframe.contentDocument.head).createChild({
22724                 tag : 'link',
22725                 rel : 'stylesheet',
22726                 type : 'text/css',
22727                 href : s
22728             });
22729         });
22730
22731         
22732     },
22733     
22734     removeStylesheets : function()
22735     {
22736         var _this = this;
22737         
22738         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22739             s.remove();
22740         });
22741     },
22742     
22743     setStyle : function(style)
22744     {
22745         Roo.get(this.iframe.contentDocument.head).createChild({
22746             tag : 'style',
22747             type : 'text/css',
22748             html : style
22749         });
22750
22751         return;
22752     }
22753     
22754     // hide stuff that is not compatible
22755     /**
22756      * @event blur
22757      * @hide
22758      */
22759     /**
22760      * @event change
22761      * @hide
22762      */
22763     /**
22764      * @event focus
22765      * @hide
22766      */
22767     /**
22768      * @event specialkey
22769      * @hide
22770      */
22771     /**
22772      * @cfg {String} fieldClass @hide
22773      */
22774     /**
22775      * @cfg {String} focusClass @hide
22776      */
22777     /**
22778      * @cfg {String} autoCreate @hide
22779      */
22780     /**
22781      * @cfg {String} inputType @hide
22782      */
22783     /**
22784      * @cfg {String} invalidClass @hide
22785      */
22786     /**
22787      * @cfg {String} invalidText @hide
22788      */
22789     /**
22790      * @cfg {String} msgFx @hide
22791      */
22792     /**
22793      * @cfg {String} validateOnBlur @hide
22794      */
22795 });
22796
22797 Roo.HtmlEditorCore.white = [
22798         'area', 'br', 'img', 'input', 'hr', 'wbr',
22799         
22800        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22801        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22802        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22803        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22804        'table',   'ul',         'xmp', 
22805        
22806        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22807       'thead',   'tr', 
22808      
22809       'dir', 'menu', 'ol', 'ul', 'dl',
22810        
22811       'embed',  'object'
22812 ];
22813
22814
22815 Roo.HtmlEditorCore.black = [
22816     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22817         'applet', // 
22818         'base',   'basefont', 'bgsound', 'blink',  'body', 
22819         'frame',  'frameset', 'head',    'html',   'ilayer', 
22820         'iframe', 'layer',  'link',     'meta',    'object',   
22821         'script', 'style' ,'title',  'xml' // clean later..
22822 ];
22823 Roo.HtmlEditorCore.clean = [
22824     'script', 'style', 'title', 'xml'
22825 ];
22826 Roo.HtmlEditorCore.remove = [
22827     'font'
22828 ];
22829 // attributes..
22830
22831 Roo.HtmlEditorCore.ablack = [
22832     'on'
22833 ];
22834     
22835 Roo.HtmlEditorCore.aclean = [ 
22836     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22837 ];
22838
22839 // protocols..
22840 Roo.HtmlEditorCore.pwhite= [
22841         'http',  'https',  'mailto'
22842 ];
22843
22844 // white listed style attributes.
22845 Roo.HtmlEditorCore.cwhite= [
22846       //  'text-align', /// default is to allow most things..
22847       
22848          
22849 //        'font-size'//??
22850 ];
22851
22852 // black listed style attributes.
22853 Roo.HtmlEditorCore.cblack= [
22854       //  'font-size' -- this can be set by the project 
22855 ];
22856
22857
22858 Roo.HtmlEditorCore.swapCodes   =[ 
22859     [    8211, "--" ], 
22860     [    8212, "--" ], 
22861     [    8216,  "'" ],  
22862     [    8217, "'" ],  
22863     [    8220, '"' ],  
22864     [    8221, '"' ],  
22865     [    8226, "*" ],  
22866     [    8230, "..." ]
22867 ]; 
22868
22869     /*
22870  * - LGPL
22871  *
22872  * HtmlEditor
22873  * 
22874  */
22875
22876 /**
22877  * @class Roo.bootstrap.HtmlEditor
22878  * @extends Roo.bootstrap.TextArea
22879  * Bootstrap HtmlEditor class
22880
22881  * @constructor
22882  * Create a new HtmlEditor
22883  * @param {Object} config The config object
22884  */
22885
22886 Roo.bootstrap.HtmlEditor = function(config){
22887     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22888     if (!this.toolbars) {
22889         this.toolbars = [];
22890     }
22891     
22892     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22893     this.addEvents({
22894             /**
22895              * @event initialize
22896              * Fires when the editor is fully initialized (including the iframe)
22897              * @param {HtmlEditor} this
22898              */
22899             initialize: true,
22900             /**
22901              * @event activate
22902              * Fires when the editor is first receives the focus. Any insertion must wait
22903              * until after this event.
22904              * @param {HtmlEditor} this
22905              */
22906             activate: true,
22907              /**
22908              * @event beforesync
22909              * Fires before the textarea is updated with content from the editor iframe. Return false
22910              * to cancel the sync.
22911              * @param {HtmlEditor} this
22912              * @param {String} html
22913              */
22914             beforesync: true,
22915              /**
22916              * @event beforepush
22917              * Fires before the iframe editor is updated with content from the textarea. Return false
22918              * to cancel the push.
22919              * @param {HtmlEditor} this
22920              * @param {String} html
22921              */
22922             beforepush: true,
22923              /**
22924              * @event sync
22925              * Fires when the textarea is updated with content from the editor iframe.
22926              * @param {HtmlEditor} this
22927              * @param {String} html
22928              */
22929             sync: true,
22930              /**
22931              * @event push
22932              * Fires when the iframe editor is updated with content from the textarea.
22933              * @param {HtmlEditor} this
22934              * @param {String} html
22935              */
22936             push: true,
22937              /**
22938              * @event editmodechange
22939              * Fires when the editor switches edit modes
22940              * @param {HtmlEditor} this
22941              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22942              */
22943             editmodechange: true,
22944             /**
22945              * @event editorevent
22946              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22947              * @param {HtmlEditor} this
22948              */
22949             editorevent: true,
22950             /**
22951              * @event firstfocus
22952              * Fires when on first focus - needed by toolbars..
22953              * @param {HtmlEditor} this
22954              */
22955             firstfocus: true,
22956             /**
22957              * @event autosave
22958              * Auto save the htmlEditor value as a file into Events
22959              * @param {HtmlEditor} this
22960              */
22961             autosave: true,
22962             /**
22963              * @event savedpreview
22964              * preview the saved version of htmlEditor
22965              * @param {HtmlEditor} this
22966              */
22967             savedpreview: true
22968         });
22969 };
22970
22971
22972 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22973     
22974     
22975       /**
22976      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22977      */
22978     toolbars : false,
22979     
22980      /**
22981     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22982     */
22983     btns : [],
22984    
22985      /**
22986      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22987      *                        Roo.resizable.
22988      */
22989     resizable : false,
22990      /**
22991      * @cfg {Number} height (in pixels)
22992      */   
22993     height: 300,
22994    /**
22995      * @cfg {Number} width (in pixels)
22996      */   
22997     width: false,
22998     
22999     /**
23000      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23001      * 
23002      */
23003     stylesheets: false,
23004     
23005     // id of frame..
23006     frameId: false,
23007     
23008     // private properties
23009     validationEvent : false,
23010     deferHeight: true,
23011     initialized : false,
23012     activated : false,
23013     
23014     onFocus : Roo.emptyFn,
23015     iframePad:3,
23016     hideMode:'offsets',
23017     
23018     tbContainer : false,
23019     
23020     bodyCls : '',
23021     
23022     toolbarContainer :function() {
23023         return this.wrap.select('.x-html-editor-tb',true).first();
23024     },
23025
23026     /**
23027      * Protected method that will not generally be called directly. It
23028      * is called when the editor creates its toolbar. Override this method if you need to
23029      * add custom toolbar buttons.
23030      * @param {HtmlEditor} editor
23031      */
23032     createToolbar : function(){
23033         Roo.log('renewing');
23034         Roo.log("create toolbars");
23035         
23036         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23037         this.toolbars[0].render(this.toolbarContainer());
23038         
23039         return;
23040         
23041 //        if (!editor.toolbars || !editor.toolbars.length) {
23042 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23043 //        }
23044 //        
23045 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23046 //            editor.toolbars[i] = Roo.factory(
23047 //                    typeof(editor.toolbars[i]) == 'string' ?
23048 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23049 //                Roo.bootstrap.HtmlEditor);
23050 //            editor.toolbars[i].init(editor);
23051 //        }
23052     },
23053
23054      
23055     // private
23056     onRender : function(ct, position)
23057     {
23058        // Roo.log("Call onRender: " + this.xtype);
23059         var _t = this;
23060         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23061       
23062         this.wrap = this.inputEl().wrap({
23063             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23064         });
23065         
23066         this.editorcore.onRender(ct, position);
23067          
23068         if (this.resizable) {
23069             this.resizeEl = new Roo.Resizable(this.wrap, {
23070                 pinned : true,
23071                 wrap: true,
23072                 dynamic : true,
23073                 minHeight : this.height,
23074                 height: this.height,
23075                 handles : this.resizable,
23076                 width: this.width,
23077                 listeners : {
23078                     resize : function(r, w, h) {
23079                         _t.onResize(w,h); // -something
23080                     }
23081                 }
23082             });
23083             
23084         }
23085         this.createToolbar(this);
23086        
23087         
23088         if(!this.width && this.resizable){
23089             this.setSize(this.wrap.getSize());
23090         }
23091         if (this.resizeEl) {
23092             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23093             // should trigger onReize..
23094         }
23095         
23096     },
23097
23098     // private
23099     onResize : function(w, h)
23100     {
23101         Roo.log('resize: ' +w + ',' + h );
23102         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23103         var ew = false;
23104         var eh = false;
23105         
23106         if(this.inputEl() ){
23107             if(typeof w == 'number'){
23108                 var aw = w - this.wrap.getFrameWidth('lr');
23109                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23110                 ew = aw;
23111             }
23112             if(typeof h == 'number'){
23113                  var tbh = -11;  // fixme it needs to tool bar size!
23114                 for (var i =0; i < this.toolbars.length;i++) {
23115                     // fixme - ask toolbars for heights?
23116                     tbh += this.toolbars[i].el.getHeight();
23117                     //if (this.toolbars[i].footer) {
23118                     //    tbh += this.toolbars[i].footer.el.getHeight();
23119                     //}
23120                 }
23121               
23122                 
23123                 
23124                 
23125                 
23126                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23127                 ah -= 5; // knock a few pixes off for look..
23128                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23129                 var eh = ah;
23130             }
23131         }
23132         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23133         this.editorcore.onResize(ew,eh);
23134         
23135     },
23136
23137     /**
23138      * Toggles the editor between standard and source edit mode.
23139      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23140      */
23141     toggleSourceEdit : function(sourceEditMode)
23142     {
23143         this.editorcore.toggleSourceEdit(sourceEditMode);
23144         
23145         if(this.editorcore.sourceEditMode){
23146             Roo.log('editor - showing textarea');
23147             
23148 //            Roo.log('in');
23149 //            Roo.log(this.syncValue());
23150             this.syncValue();
23151             this.inputEl().removeClass(['hide', 'x-hidden']);
23152             this.inputEl().dom.removeAttribute('tabIndex');
23153             this.inputEl().focus();
23154         }else{
23155             Roo.log('editor - hiding textarea');
23156 //            Roo.log('out')
23157 //            Roo.log(this.pushValue()); 
23158             this.pushValue();
23159             
23160             this.inputEl().addClass(['hide', 'x-hidden']);
23161             this.inputEl().dom.setAttribute('tabIndex', -1);
23162             //this.deferFocus();
23163         }
23164          
23165         if(this.resizable){
23166             this.setSize(this.wrap.getSize());
23167         }
23168         
23169         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23170     },
23171  
23172     // private (for BoxComponent)
23173     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23174
23175     // private (for BoxComponent)
23176     getResizeEl : function(){
23177         return this.wrap;
23178     },
23179
23180     // private (for BoxComponent)
23181     getPositionEl : function(){
23182         return this.wrap;
23183     },
23184
23185     // private
23186     initEvents : function(){
23187         this.originalValue = this.getValue();
23188     },
23189
23190 //    /**
23191 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23192 //     * @method
23193 //     */
23194 //    markInvalid : Roo.emptyFn,
23195 //    /**
23196 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23197 //     * @method
23198 //     */
23199 //    clearInvalid : Roo.emptyFn,
23200
23201     setValue : function(v){
23202         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23203         this.editorcore.pushValue();
23204     },
23205
23206      
23207     // private
23208     deferFocus : function(){
23209         this.focus.defer(10, this);
23210     },
23211
23212     // doc'ed in Field
23213     focus : function(){
23214         this.editorcore.focus();
23215         
23216     },
23217       
23218
23219     // private
23220     onDestroy : function(){
23221         
23222         
23223         
23224         if(this.rendered){
23225             
23226             for (var i =0; i < this.toolbars.length;i++) {
23227                 // fixme - ask toolbars for heights?
23228                 this.toolbars[i].onDestroy();
23229             }
23230             
23231             this.wrap.dom.innerHTML = '';
23232             this.wrap.remove();
23233         }
23234     },
23235
23236     // private
23237     onFirstFocus : function(){
23238         //Roo.log("onFirstFocus");
23239         this.editorcore.onFirstFocus();
23240          for (var i =0; i < this.toolbars.length;i++) {
23241             this.toolbars[i].onFirstFocus();
23242         }
23243         
23244     },
23245     
23246     // private
23247     syncValue : function()
23248     {   
23249         this.editorcore.syncValue();
23250     },
23251     
23252     pushValue : function()
23253     {   
23254         this.editorcore.pushValue();
23255     }
23256      
23257     
23258     // hide stuff that is not compatible
23259     /**
23260      * @event blur
23261      * @hide
23262      */
23263     /**
23264      * @event change
23265      * @hide
23266      */
23267     /**
23268      * @event focus
23269      * @hide
23270      */
23271     /**
23272      * @event specialkey
23273      * @hide
23274      */
23275     /**
23276      * @cfg {String} fieldClass @hide
23277      */
23278     /**
23279      * @cfg {String} focusClass @hide
23280      */
23281     /**
23282      * @cfg {String} autoCreate @hide
23283      */
23284     /**
23285      * @cfg {String} inputType @hide
23286      */
23287     /**
23288      * @cfg {String} invalidClass @hide
23289      */
23290     /**
23291      * @cfg {String} invalidText @hide
23292      */
23293     /**
23294      * @cfg {String} msgFx @hide
23295      */
23296     /**
23297      * @cfg {String} validateOnBlur @hide
23298      */
23299 });
23300  
23301     
23302    
23303    
23304    
23305       
23306 Roo.namespace('Roo.bootstrap.htmleditor');
23307 /**
23308  * @class Roo.bootstrap.HtmlEditorToolbar1
23309  * Basic Toolbar
23310  * 
23311  * Usage:
23312  *
23313  new Roo.bootstrap.HtmlEditor({
23314     ....
23315     toolbars : [
23316         new Roo.bootstrap.HtmlEditorToolbar1({
23317             disable : { fonts: 1 , format: 1, ..., ... , ...],
23318             btns : [ .... ]
23319         })
23320     }
23321      
23322  * 
23323  * @cfg {Object} disable List of elements to disable..
23324  * @cfg {Array} btns List of additional buttons.
23325  * 
23326  * 
23327  * NEEDS Extra CSS? 
23328  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23329  */
23330  
23331 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23332 {
23333     
23334     Roo.apply(this, config);
23335     
23336     // default disabled, based on 'good practice'..
23337     this.disable = this.disable || {};
23338     Roo.applyIf(this.disable, {
23339         fontSize : true,
23340         colors : true,
23341         specialElements : true
23342     });
23343     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23344     
23345     this.editor = config.editor;
23346     this.editorcore = config.editor.editorcore;
23347     
23348     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23349     
23350     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23351     // dont call parent... till later.
23352 }
23353 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23354      
23355     bar : true,
23356     
23357     editor : false,
23358     editorcore : false,
23359     
23360     
23361     formats : [
23362         "p" ,  
23363         "h1","h2","h3","h4","h5","h6", 
23364         "pre", "code", 
23365         "abbr", "acronym", "address", "cite", "samp", "var",
23366         'div','span'
23367     ],
23368     
23369     onRender : function(ct, position)
23370     {
23371        // Roo.log("Call onRender: " + this.xtype);
23372         
23373        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23374        Roo.log(this.el);
23375        this.el.dom.style.marginBottom = '0';
23376        var _this = this;
23377        var editorcore = this.editorcore;
23378        var editor= this.editor;
23379        
23380        var children = [];
23381        var btn = function(id,cmd , toggle, handler, html){
23382        
23383             var  event = toggle ? 'toggle' : 'click';
23384        
23385             var a = {
23386                 size : 'sm',
23387                 xtype: 'Button',
23388                 xns: Roo.bootstrap,
23389                 glyphicon : id,
23390                 cmd : id || cmd,
23391                 enableToggle:toggle !== false,
23392                 html : html || '',
23393                 pressed : toggle ? false : null,
23394                 listeners : {}
23395             };
23396             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23397                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23398             };
23399             children.push(a);
23400             return a;
23401        }
23402        
23403     //    var cb_box = function...
23404         
23405         var style = {
23406                 xtype: 'Button',
23407                 size : 'sm',
23408                 xns: Roo.bootstrap,
23409                 glyphicon : 'font',
23410                 //html : 'submit'
23411                 menu : {
23412                     xtype: 'Menu',
23413                     xns: Roo.bootstrap,
23414                     items:  []
23415                 }
23416         };
23417         Roo.each(this.formats, function(f) {
23418             style.menu.items.push({
23419                 xtype :'MenuItem',
23420                 xns: Roo.bootstrap,
23421                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23422                 tagname : f,
23423                 listeners : {
23424                     click : function()
23425                     {
23426                         editorcore.insertTag(this.tagname);
23427                         editor.focus();
23428                     }
23429                 }
23430                 
23431             });
23432         });
23433         children.push(style);   
23434         
23435         btn('bold',false,true);
23436         btn('italic',false,true);
23437         btn('align-left', 'justifyleft',true);
23438         btn('align-center', 'justifycenter',true);
23439         btn('align-right' , 'justifyright',true);
23440         btn('link', false, false, function(btn) {
23441             //Roo.log("create link?");
23442             var url = prompt(this.createLinkText, this.defaultLinkValue);
23443             if(url && url != 'http:/'+'/'){
23444                 this.editorcore.relayCmd('createlink', url);
23445             }
23446         }),
23447         btn('list','insertunorderedlist',true);
23448         btn('pencil', false,true, function(btn){
23449                 Roo.log(this);
23450                 this.toggleSourceEdit(btn.pressed);
23451         });
23452         
23453         if (this.editor.btns.length > 0) {
23454             for (var i = 0; i<this.editor.btns.length; i++) {
23455                 children.push(this.editor.btns[i]);
23456             }
23457         }
23458         
23459         /*
23460         var cog = {
23461                 xtype: 'Button',
23462                 size : 'sm',
23463                 xns: Roo.bootstrap,
23464                 glyphicon : 'cog',
23465                 //html : 'submit'
23466                 menu : {
23467                     xtype: 'Menu',
23468                     xns: Roo.bootstrap,
23469                     items:  []
23470                 }
23471         };
23472         
23473         cog.menu.items.push({
23474             xtype :'MenuItem',
23475             xns: Roo.bootstrap,
23476             html : Clean styles,
23477             tagname : f,
23478             listeners : {
23479                 click : function()
23480                 {
23481                     editorcore.insertTag(this.tagname);
23482                     editor.focus();
23483                 }
23484             }
23485             
23486         });
23487        */
23488         
23489          
23490        this.xtype = 'NavSimplebar';
23491         
23492         for(var i=0;i< children.length;i++) {
23493             
23494             this.buttons.add(this.addxtypeChild(children[i]));
23495             
23496         }
23497         
23498         editor.on('editorevent', this.updateToolbar, this);
23499     },
23500     onBtnClick : function(id)
23501     {
23502        this.editorcore.relayCmd(id);
23503        this.editorcore.focus();
23504     },
23505     
23506     /**
23507      * Protected method that will not generally be called directly. It triggers
23508      * a toolbar update by reading the markup state of the current selection in the editor.
23509      */
23510     updateToolbar: function(){
23511
23512         if(!this.editorcore.activated){
23513             this.editor.onFirstFocus(); // is this neeed?
23514             return;
23515         }
23516
23517         var btns = this.buttons; 
23518         var doc = this.editorcore.doc;
23519         btns.get('bold').setActive(doc.queryCommandState('bold'));
23520         btns.get('italic').setActive(doc.queryCommandState('italic'));
23521         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23522         
23523         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23524         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23525         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23526         
23527         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23528         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23529          /*
23530         
23531         var ans = this.editorcore.getAllAncestors();
23532         if (this.formatCombo) {
23533             
23534             
23535             var store = this.formatCombo.store;
23536             this.formatCombo.setValue("");
23537             for (var i =0; i < ans.length;i++) {
23538                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23539                     // select it..
23540                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23541                     break;
23542                 }
23543             }
23544         }
23545         
23546         
23547         
23548         // hides menus... - so this cant be on a menu...
23549         Roo.bootstrap.MenuMgr.hideAll();
23550         */
23551         Roo.bootstrap.MenuMgr.hideAll();
23552         //this.editorsyncValue();
23553     },
23554     onFirstFocus: function() {
23555         this.buttons.each(function(item){
23556            item.enable();
23557         });
23558     },
23559     toggleSourceEdit : function(sourceEditMode){
23560         
23561           
23562         if(sourceEditMode){
23563             Roo.log("disabling buttons");
23564            this.buttons.each( function(item){
23565                 if(item.cmd != 'pencil'){
23566                     item.disable();
23567                 }
23568             });
23569           
23570         }else{
23571             Roo.log("enabling buttons");
23572             if(this.editorcore.initialized){
23573                 this.buttons.each( function(item){
23574                     item.enable();
23575                 });
23576             }
23577             
23578         }
23579         Roo.log("calling toggole on editor");
23580         // tell the editor that it's been pressed..
23581         this.editor.toggleSourceEdit(sourceEditMode);
23582        
23583     }
23584 });
23585
23586
23587
23588
23589
23590 /**
23591  * @class Roo.bootstrap.Table.AbstractSelectionModel
23592  * @extends Roo.util.Observable
23593  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23594  * implemented by descendant classes.  This class should not be directly instantiated.
23595  * @constructor
23596  */
23597 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23598     this.locked = false;
23599     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23600 };
23601
23602
23603 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23604     /** @ignore Called by the grid automatically. Do not call directly. */
23605     init : function(grid){
23606         this.grid = grid;
23607         this.initEvents();
23608     },
23609
23610     /**
23611      * Locks the selections.
23612      */
23613     lock : function(){
23614         this.locked = true;
23615     },
23616
23617     /**
23618      * Unlocks the selections.
23619      */
23620     unlock : function(){
23621         this.locked = false;
23622     },
23623
23624     /**
23625      * Returns true if the selections are locked.
23626      * @return {Boolean}
23627      */
23628     isLocked : function(){
23629         return this.locked;
23630     }
23631 });
23632 /**
23633  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23634  * @class Roo.bootstrap.Table.RowSelectionModel
23635  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23636  * It supports multiple selections and keyboard selection/navigation. 
23637  * @constructor
23638  * @param {Object} config
23639  */
23640
23641 Roo.bootstrap.Table.RowSelectionModel = function(config){
23642     Roo.apply(this, config);
23643     this.selections = new Roo.util.MixedCollection(false, function(o){
23644         return o.id;
23645     });
23646
23647     this.last = false;
23648     this.lastActive = false;
23649
23650     this.addEvents({
23651         /**
23652              * @event selectionchange
23653              * Fires when the selection changes
23654              * @param {SelectionModel} this
23655              */
23656             "selectionchange" : true,
23657         /**
23658              * @event afterselectionchange
23659              * Fires after the selection changes (eg. by key press or clicking)
23660              * @param {SelectionModel} this
23661              */
23662             "afterselectionchange" : true,
23663         /**
23664              * @event beforerowselect
23665              * Fires when a row is selected being selected, return false to cancel.
23666              * @param {SelectionModel} this
23667              * @param {Number} rowIndex The selected index
23668              * @param {Boolean} keepExisting False if other selections will be cleared
23669              */
23670             "beforerowselect" : true,
23671         /**
23672              * @event rowselect
23673              * Fires when a row is selected.
23674              * @param {SelectionModel} this
23675              * @param {Number} rowIndex The selected index
23676              * @param {Roo.data.Record} r The record
23677              */
23678             "rowselect" : true,
23679         /**
23680              * @event rowdeselect
23681              * Fires when a row is deselected.
23682              * @param {SelectionModel} this
23683              * @param {Number} rowIndex The selected index
23684              */
23685         "rowdeselect" : true
23686     });
23687     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23688     this.locked = false;
23689  };
23690
23691 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23692     /**
23693      * @cfg {Boolean} singleSelect
23694      * True to allow selection of only one row at a time (defaults to false)
23695      */
23696     singleSelect : false,
23697
23698     // private
23699     initEvents : function()
23700     {
23701
23702         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23703         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23704         //}else{ // allow click to work like normal
23705          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23706         //}
23707         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23708         this.grid.on("rowclick", this.handleMouseDown, this);
23709         
23710         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23711             "up" : function(e){
23712                 if(!e.shiftKey){
23713                     this.selectPrevious(e.shiftKey);
23714                 }else if(this.last !== false && this.lastActive !== false){
23715                     var last = this.last;
23716                     this.selectRange(this.last,  this.lastActive-1);
23717                     this.grid.getView().focusRow(this.lastActive);
23718                     if(last !== false){
23719                         this.last = last;
23720                     }
23721                 }else{
23722                     this.selectFirstRow();
23723                 }
23724                 this.fireEvent("afterselectionchange", this);
23725             },
23726             "down" : function(e){
23727                 if(!e.shiftKey){
23728                     this.selectNext(e.shiftKey);
23729                 }else if(this.last !== false && this.lastActive !== false){
23730                     var last = this.last;
23731                     this.selectRange(this.last,  this.lastActive+1);
23732                     this.grid.getView().focusRow(this.lastActive);
23733                     if(last !== false){
23734                         this.last = last;
23735                     }
23736                 }else{
23737                     this.selectFirstRow();
23738                 }
23739                 this.fireEvent("afterselectionchange", this);
23740             },
23741             scope: this
23742         });
23743         this.grid.store.on('load', function(){
23744             this.selections.clear();
23745         },this);
23746         /*
23747         var view = this.grid.view;
23748         view.on("refresh", this.onRefresh, this);
23749         view.on("rowupdated", this.onRowUpdated, this);
23750         view.on("rowremoved", this.onRemove, this);
23751         */
23752     },
23753
23754     // private
23755     onRefresh : function()
23756     {
23757         var ds = this.grid.store, i, v = this.grid.view;
23758         var s = this.selections;
23759         s.each(function(r){
23760             if((i = ds.indexOfId(r.id)) != -1){
23761                 v.onRowSelect(i);
23762             }else{
23763                 s.remove(r);
23764             }
23765         });
23766     },
23767
23768     // private
23769     onRemove : function(v, index, r){
23770         this.selections.remove(r);
23771     },
23772
23773     // private
23774     onRowUpdated : function(v, index, r){
23775         if(this.isSelected(r)){
23776             v.onRowSelect(index);
23777         }
23778     },
23779
23780     /**
23781      * Select records.
23782      * @param {Array} records The records to select
23783      * @param {Boolean} keepExisting (optional) True to keep existing selections
23784      */
23785     selectRecords : function(records, keepExisting)
23786     {
23787         if(!keepExisting){
23788             this.clearSelections();
23789         }
23790             var ds = this.grid.store;
23791         for(var i = 0, len = records.length; i < len; i++){
23792             this.selectRow(ds.indexOf(records[i]), true);
23793         }
23794     },
23795
23796     /**
23797      * Gets the number of selected rows.
23798      * @return {Number}
23799      */
23800     getCount : function(){
23801         return this.selections.length;
23802     },
23803
23804     /**
23805      * Selects the first row in the grid.
23806      */
23807     selectFirstRow : function(){
23808         this.selectRow(0);
23809     },
23810
23811     /**
23812      * Select the last row.
23813      * @param {Boolean} keepExisting (optional) True to keep existing selections
23814      */
23815     selectLastRow : function(keepExisting){
23816         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23817         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23818     },
23819
23820     /**
23821      * Selects the row immediately following the last selected row.
23822      * @param {Boolean} keepExisting (optional) True to keep existing selections
23823      */
23824     selectNext : function(keepExisting)
23825     {
23826             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23827             this.selectRow(this.last+1, keepExisting);
23828             this.grid.getView().focusRow(this.last);
23829         }
23830     },
23831
23832     /**
23833      * Selects the row that precedes the last selected row.
23834      * @param {Boolean} keepExisting (optional) True to keep existing selections
23835      */
23836     selectPrevious : function(keepExisting){
23837         if(this.last){
23838             this.selectRow(this.last-1, keepExisting);
23839             this.grid.getView().focusRow(this.last);
23840         }
23841     },
23842
23843     /**
23844      * Returns the selected records
23845      * @return {Array} Array of selected records
23846      */
23847     getSelections : function(){
23848         return [].concat(this.selections.items);
23849     },
23850
23851     /**
23852      * Returns the first selected record.
23853      * @return {Record}
23854      */
23855     getSelected : function(){
23856         return this.selections.itemAt(0);
23857     },
23858
23859
23860     /**
23861      * Clears all selections.
23862      */
23863     clearSelections : function(fast)
23864     {
23865         if(this.locked) {
23866             return;
23867         }
23868         if(fast !== true){
23869                 var ds = this.grid.store;
23870             var s = this.selections;
23871             s.each(function(r){
23872                 this.deselectRow(ds.indexOfId(r.id));
23873             }, this);
23874             s.clear();
23875         }else{
23876             this.selections.clear();
23877         }
23878         this.last = false;
23879     },
23880
23881
23882     /**
23883      * Selects all rows.
23884      */
23885     selectAll : function(){
23886         if(this.locked) {
23887             return;
23888         }
23889         this.selections.clear();
23890         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23891             this.selectRow(i, true);
23892         }
23893     },
23894
23895     /**
23896      * Returns True if there is a selection.
23897      * @return {Boolean}
23898      */
23899     hasSelection : function(){
23900         return this.selections.length > 0;
23901     },
23902
23903     /**
23904      * Returns True if the specified row is selected.
23905      * @param {Number/Record} record The record or index of the record to check
23906      * @return {Boolean}
23907      */
23908     isSelected : function(index){
23909             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23910         return (r && this.selections.key(r.id) ? true : false);
23911     },
23912
23913     /**
23914      * Returns True if the specified record id is selected.
23915      * @param {String} id The id of record to check
23916      * @return {Boolean}
23917      */
23918     isIdSelected : function(id){
23919         return (this.selections.key(id) ? true : false);
23920     },
23921
23922
23923     // private
23924     handleMouseDBClick : function(e, t){
23925         
23926     },
23927     // private
23928     handleMouseDown : function(e, t)
23929     {
23930             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23931         if(this.isLocked() || rowIndex < 0 ){
23932             return;
23933         };
23934         if(e.shiftKey && this.last !== false){
23935             var last = this.last;
23936             this.selectRange(last, rowIndex, e.ctrlKey);
23937             this.last = last; // reset the last
23938             t.focus();
23939     
23940         }else{
23941             var isSelected = this.isSelected(rowIndex);
23942             //Roo.log("select row:" + rowIndex);
23943             if(isSelected){
23944                 this.deselectRow(rowIndex);
23945             } else {
23946                         this.selectRow(rowIndex, true);
23947             }
23948     
23949             /*
23950                 if(e.button !== 0 && isSelected){
23951                 alert('rowIndex 2: ' + rowIndex);
23952                     view.focusRow(rowIndex);
23953                 }else if(e.ctrlKey && isSelected){
23954                     this.deselectRow(rowIndex);
23955                 }else if(!isSelected){
23956                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23957                     view.focusRow(rowIndex);
23958                 }
23959             */
23960         }
23961         this.fireEvent("afterselectionchange", this);
23962     },
23963     // private
23964     handleDragableRowClick :  function(grid, rowIndex, e) 
23965     {
23966         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23967             this.selectRow(rowIndex, false);
23968             grid.view.focusRow(rowIndex);
23969              this.fireEvent("afterselectionchange", this);
23970         }
23971     },
23972     
23973     /**
23974      * Selects multiple rows.
23975      * @param {Array} rows Array of the indexes of the row to select
23976      * @param {Boolean} keepExisting (optional) True to keep existing selections
23977      */
23978     selectRows : function(rows, keepExisting){
23979         if(!keepExisting){
23980             this.clearSelections();
23981         }
23982         for(var i = 0, len = rows.length; i < len; i++){
23983             this.selectRow(rows[i], true);
23984         }
23985     },
23986
23987     /**
23988      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23989      * @param {Number} startRow The index of the first row in the range
23990      * @param {Number} endRow The index of the last row in the range
23991      * @param {Boolean} keepExisting (optional) True to retain existing selections
23992      */
23993     selectRange : function(startRow, endRow, keepExisting){
23994         if(this.locked) {
23995             return;
23996         }
23997         if(!keepExisting){
23998             this.clearSelections();
23999         }
24000         if(startRow <= endRow){
24001             for(var i = startRow; i <= endRow; i++){
24002                 this.selectRow(i, true);
24003             }
24004         }else{
24005             for(var i = startRow; i >= endRow; i--){
24006                 this.selectRow(i, true);
24007             }
24008         }
24009     },
24010
24011     /**
24012      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24013      * @param {Number} startRow The index of the first row in the range
24014      * @param {Number} endRow The index of the last row in the range
24015      */
24016     deselectRange : function(startRow, endRow, preventViewNotify){
24017         if(this.locked) {
24018             return;
24019         }
24020         for(var i = startRow; i <= endRow; i++){
24021             this.deselectRow(i, preventViewNotify);
24022         }
24023     },
24024
24025     /**
24026      * Selects a row.
24027      * @param {Number} row The index of the row to select
24028      * @param {Boolean} keepExisting (optional) True to keep existing selections
24029      */
24030     selectRow : function(index, keepExisting, preventViewNotify)
24031     {
24032             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24033             return;
24034         }
24035         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24036             if(!keepExisting || this.singleSelect){
24037                 this.clearSelections();
24038             }
24039             
24040             var r = this.grid.store.getAt(index);
24041             //console.log('selectRow - record id :' + r.id);
24042             
24043             this.selections.add(r);
24044             this.last = this.lastActive = index;
24045             if(!preventViewNotify){
24046                 var proxy = new Roo.Element(
24047                                 this.grid.getRowDom(index)
24048                 );
24049                 proxy.addClass('bg-info info');
24050             }
24051             this.fireEvent("rowselect", this, index, r);
24052             this.fireEvent("selectionchange", this);
24053         }
24054     },
24055
24056     /**
24057      * Deselects a row.
24058      * @param {Number} row The index of the row to deselect
24059      */
24060     deselectRow : function(index, preventViewNotify)
24061     {
24062         if(this.locked) {
24063             return;
24064         }
24065         if(this.last == index){
24066             this.last = false;
24067         }
24068         if(this.lastActive == index){
24069             this.lastActive = false;
24070         }
24071         
24072         var r = this.grid.store.getAt(index);
24073         if (!r) {
24074             return;
24075         }
24076         
24077         this.selections.remove(r);
24078         //.console.log('deselectRow - record id :' + r.id);
24079         if(!preventViewNotify){
24080         
24081             var proxy = new Roo.Element(
24082                 this.grid.getRowDom(index)
24083             );
24084             proxy.removeClass('bg-info info');
24085         }
24086         this.fireEvent("rowdeselect", this, index);
24087         this.fireEvent("selectionchange", this);
24088     },
24089
24090     // private
24091     restoreLast : function(){
24092         if(this._last){
24093             this.last = this._last;
24094         }
24095     },
24096
24097     // private
24098     acceptsNav : function(row, col, cm){
24099         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24100     },
24101
24102     // private
24103     onEditorKey : function(field, e){
24104         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24105         if(k == e.TAB){
24106             e.stopEvent();
24107             ed.completeEdit();
24108             if(e.shiftKey){
24109                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24110             }else{
24111                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24112             }
24113         }else if(k == e.ENTER && !e.ctrlKey){
24114             e.stopEvent();
24115             ed.completeEdit();
24116             if(e.shiftKey){
24117                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24118             }else{
24119                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24120             }
24121         }else if(k == e.ESC){
24122             ed.cancelEdit();
24123         }
24124         if(newCell){
24125             g.startEditing(newCell[0], newCell[1]);
24126         }
24127     }
24128 });
24129 /*
24130  * Based on:
24131  * Ext JS Library 1.1.1
24132  * Copyright(c) 2006-2007, Ext JS, LLC.
24133  *
24134  * Originally Released Under LGPL - original licence link has changed is not relivant.
24135  *
24136  * Fork - LGPL
24137  * <script type="text/javascript">
24138  */
24139  
24140 /**
24141  * @class Roo.bootstrap.PagingToolbar
24142  * @extends Roo.bootstrap.NavSimplebar
24143  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24144  * @constructor
24145  * Create a new PagingToolbar
24146  * @param {Object} config The config object
24147  * @param {Roo.data.Store} store
24148  */
24149 Roo.bootstrap.PagingToolbar = function(config)
24150 {
24151     // old args format still supported... - xtype is prefered..
24152         // created from xtype...
24153     
24154     this.ds = config.dataSource;
24155     
24156     if (config.store && !this.ds) {
24157         this.store= Roo.factory(config.store, Roo.data);
24158         this.ds = this.store;
24159         this.ds.xmodule = this.xmodule || false;
24160     }
24161     
24162     this.toolbarItems = [];
24163     if (config.items) {
24164         this.toolbarItems = config.items;
24165     }
24166     
24167     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24168     
24169     this.cursor = 0;
24170     
24171     if (this.ds) { 
24172         this.bind(this.ds);
24173     }
24174     
24175     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24176     
24177 };
24178
24179 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24180     /**
24181      * @cfg {Roo.data.Store} dataSource
24182      * The underlying data store providing the paged data
24183      */
24184     /**
24185      * @cfg {String/HTMLElement/Element} container
24186      * container The id or element that will contain the toolbar
24187      */
24188     /**
24189      * @cfg {Boolean} displayInfo
24190      * True to display the displayMsg (defaults to false)
24191      */
24192     /**
24193      * @cfg {Number} pageSize
24194      * The number of records to display per page (defaults to 20)
24195      */
24196     pageSize: 20,
24197     /**
24198      * @cfg {String} displayMsg
24199      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24200      */
24201     displayMsg : 'Displaying {0} - {1} of {2}',
24202     /**
24203      * @cfg {String} emptyMsg
24204      * The message to display when no records are found (defaults to "No data to display")
24205      */
24206     emptyMsg : 'No data to display',
24207     /**
24208      * Customizable piece of the default paging text (defaults to "Page")
24209      * @type String
24210      */
24211     beforePageText : "Page",
24212     /**
24213      * Customizable piece of the default paging text (defaults to "of %0")
24214      * @type String
24215      */
24216     afterPageText : "of {0}",
24217     /**
24218      * Customizable piece of the default paging text (defaults to "First Page")
24219      * @type String
24220      */
24221     firstText : "First Page",
24222     /**
24223      * Customizable piece of the default paging text (defaults to "Previous Page")
24224      * @type String
24225      */
24226     prevText : "Previous Page",
24227     /**
24228      * Customizable piece of the default paging text (defaults to "Next Page")
24229      * @type String
24230      */
24231     nextText : "Next Page",
24232     /**
24233      * Customizable piece of the default paging text (defaults to "Last Page")
24234      * @type String
24235      */
24236     lastText : "Last Page",
24237     /**
24238      * Customizable piece of the default paging text (defaults to "Refresh")
24239      * @type String
24240      */
24241     refreshText : "Refresh",
24242
24243     buttons : false,
24244     // private
24245     onRender : function(ct, position) 
24246     {
24247         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24248         this.navgroup.parentId = this.id;
24249         this.navgroup.onRender(this.el, null);
24250         // add the buttons to the navgroup
24251         
24252         if(this.displayInfo){
24253             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24254             this.displayEl = this.el.select('.x-paging-info', true).first();
24255 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24256 //            this.displayEl = navel.el.select('span',true).first();
24257         }
24258         
24259         var _this = this;
24260         
24261         if(this.buttons){
24262             Roo.each(_this.buttons, function(e){ // this might need to use render????
24263                Roo.factory(e).onRender(_this.el, null);
24264             });
24265         }
24266             
24267         Roo.each(_this.toolbarItems, function(e) {
24268             _this.navgroup.addItem(e);
24269         });
24270         
24271         
24272         this.first = this.navgroup.addItem({
24273             tooltip: this.firstText,
24274             cls: "prev",
24275             icon : 'fa fa-backward',
24276             disabled: true,
24277             preventDefault: true,
24278             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24279         });
24280         
24281         this.prev =  this.navgroup.addItem({
24282             tooltip: this.prevText,
24283             cls: "prev",
24284             icon : 'fa fa-step-backward',
24285             disabled: true,
24286             preventDefault: true,
24287             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24288         });
24289     //this.addSeparator();
24290         
24291         
24292         var field = this.navgroup.addItem( {
24293             tagtype : 'span',
24294             cls : 'x-paging-position',
24295             
24296             html : this.beforePageText  +
24297                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24298                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24299          } ); //?? escaped?
24300         
24301         this.field = field.el.select('input', true).first();
24302         this.field.on("keydown", this.onPagingKeydown, this);
24303         this.field.on("focus", function(){this.dom.select();});
24304     
24305     
24306         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24307         //this.field.setHeight(18);
24308         //this.addSeparator();
24309         this.next = this.navgroup.addItem({
24310             tooltip: this.nextText,
24311             cls: "next",
24312             html : ' <i class="fa fa-step-forward">',
24313             disabled: true,
24314             preventDefault: true,
24315             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24316         });
24317         this.last = this.navgroup.addItem({
24318             tooltip: this.lastText,
24319             icon : 'fa fa-forward',
24320             cls: "next",
24321             disabled: true,
24322             preventDefault: true,
24323             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24324         });
24325     //this.addSeparator();
24326         this.loading = this.navgroup.addItem({
24327             tooltip: this.refreshText,
24328             icon: 'fa fa-refresh',
24329             preventDefault: true,
24330             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24331         });
24332         
24333     },
24334
24335     // private
24336     updateInfo : function(){
24337         if(this.displayEl){
24338             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24339             var msg = count == 0 ?
24340                 this.emptyMsg :
24341                 String.format(
24342                     this.displayMsg,
24343                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24344                 );
24345             this.displayEl.update(msg);
24346         }
24347     },
24348
24349     // private
24350     onLoad : function(ds, r, o)
24351     {
24352         this.cursor = o.params ? o.params.start : 0;
24353         var d = this.getPageData(),
24354             ap = d.activePage,
24355             ps = d.pages;
24356         
24357         
24358         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24359         this.field.dom.value = ap;
24360         this.first.setDisabled(ap == 1);
24361         this.prev.setDisabled(ap == 1);
24362         this.next.setDisabled(ap == ps);
24363         this.last.setDisabled(ap == ps);
24364         this.loading.enable();
24365         this.updateInfo();
24366     },
24367
24368     // private
24369     getPageData : function(){
24370         var total = this.ds.getTotalCount();
24371         return {
24372             total : total,
24373             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24374             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24375         };
24376     },
24377
24378     // private
24379     onLoadError : function(){
24380         this.loading.enable();
24381     },
24382
24383     // private
24384     onPagingKeydown : function(e){
24385         var k = e.getKey();
24386         var d = this.getPageData();
24387         if(k == e.RETURN){
24388             var v = this.field.dom.value, pageNum;
24389             if(!v || isNaN(pageNum = parseInt(v, 10))){
24390                 this.field.dom.value = d.activePage;
24391                 return;
24392             }
24393             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24394             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24395             e.stopEvent();
24396         }
24397         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))
24398         {
24399           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24400           this.field.dom.value = pageNum;
24401           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24402           e.stopEvent();
24403         }
24404         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24405         {
24406           var v = this.field.dom.value, pageNum; 
24407           var increment = (e.shiftKey) ? 10 : 1;
24408           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24409                 increment *= -1;
24410           }
24411           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24412             this.field.dom.value = d.activePage;
24413             return;
24414           }
24415           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24416           {
24417             this.field.dom.value = parseInt(v, 10) + increment;
24418             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24419             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24420           }
24421           e.stopEvent();
24422         }
24423     },
24424
24425     // private
24426     beforeLoad : function(){
24427         if(this.loading){
24428             this.loading.disable();
24429         }
24430     },
24431
24432     // private
24433     onClick : function(which){
24434         
24435         var ds = this.ds;
24436         if (!ds) {
24437             return;
24438         }
24439         
24440         switch(which){
24441             case "first":
24442                 ds.load({params:{start: 0, limit: this.pageSize}});
24443             break;
24444             case "prev":
24445                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24446             break;
24447             case "next":
24448                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24449             break;
24450             case "last":
24451                 var total = ds.getTotalCount();
24452                 var extra = total % this.pageSize;
24453                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24454                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24455             break;
24456             case "refresh":
24457                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24458             break;
24459         }
24460     },
24461
24462     /**
24463      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24464      * @param {Roo.data.Store} store The data store to unbind
24465      */
24466     unbind : function(ds){
24467         ds.un("beforeload", this.beforeLoad, this);
24468         ds.un("load", this.onLoad, this);
24469         ds.un("loadexception", this.onLoadError, this);
24470         ds.un("remove", this.updateInfo, this);
24471         ds.un("add", this.updateInfo, this);
24472         this.ds = undefined;
24473     },
24474
24475     /**
24476      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24477      * @param {Roo.data.Store} store The data store to bind
24478      */
24479     bind : function(ds){
24480         ds.on("beforeload", this.beforeLoad, this);
24481         ds.on("load", this.onLoad, this);
24482         ds.on("loadexception", this.onLoadError, this);
24483         ds.on("remove", this.updateInfo, this);
24484         ds.on("add", this.updateInfo, this);
24485         this.ds = ds;
24486     }
24487 });/*
24488  * - LGPL
24489  *
24490  * element
24491  * 
24492  */
24493
24494 /**
24495  * @class Roo.bootstrap.MessageBar
24496  * @extends Roo.bootstrap.Component
24497  * Bootstrap MessageBar class
24498  * @cfg {String} html contents of the MessageBar
24499  * @cfg {String} weight (info | success | warning | danger) default info
24500  * @cfg {String} beforeClass insert the bar before the given class
24501  * @cfg {Boolean} closable (true | false) default false
24502  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24503  * 
24504  * @constructor
24505  * Create a new Element
24506  * @param {Object} config The config object
24507  */
24508
24509 Roo.bootstrap.MessageBar = function(config){
24510     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24511 };
24512
24513 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24514     
24515     html: '',
24516     weight: 'info',
24517     closable: false,
24518     fixed: false,
24519     beforeClass: 'bootstrap-sticky-wrap',
24520     
24521     getAutoCreate : function(){
24522         
24523         var cfg = {
24524             tag: 'div',
24525             cls: 'alert alert-dismissable alert-' + this.weight,
24526             cn: [
24527                 {
24528                     tag: 'span',
24529                     cls: 'message',
24530                     html: this.html || ''
24531                 }
24532             ]
24533         };
24534         
24535         if(this.fixed){
24536             cfg.cls += ' alert-messages-fixed';
24537         }
24538         
24539         if(this.closable){
24540             cfg.cn.push({
24541                 tag: 'button',
24542                 cls: 'close',
24543                 html: 'x'
24544             });
24545         }
24546         
24547         return cfg;
24548     },
24549     
24550     onRender : function(ct, position)
24551     {
24552         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24553         
24554         if(!this.el){
24555             var cfg = Roo.apply({},  this.getAutoCreate());
24556             cfg.id = Roo.id();
24557             
24558             if (this.cls) {
24559                 cfg.cls += ' ' + this.cls;
24560             }
24561             if (this.style) {
24562                 cfg.style = this.style;
24563             }
24564             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24565             
24566             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24567         }
24568         
24569         this.el.select('>button.close').on('click', this.hide, this);
24570         
24571     },
24572     
24573     show : function()
24574     {
24575         if (!this.rendered) {
24576             this.render();
24577         }
24578         
24579         this.el.show();
24580         
24581         this.fireEvent('show', this);
24582         
24583     },
24584     
24585     hide : function()
24586     {
24587         if (!this.rendered) {
24588             this.render();
24589         }
24590         
24591         this.el.hide();
24592         
24593         this.fireEvent('hide', this);
24594     },
24595     
24596     update : function()
24597     {
24598 //        var e = this.el.dom.firstChild;
24599 //        
24600 //        if(this.closable){
24601 //            e = e.nextSibling;
24602 //        }
24603 //        
24604 //        e.data = this.html || '';
24605
24606         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24607     }
24608    
24609 });
24610
24611  
24612
24613      /*
24614  * - LGPL
24615  *
24616  * Graph
24617  * 
24618  */
24619
24620
24621 /**
24622  * @class Roo.bootstrap.Graph
24623  * @extends Roo.bootstrap.Component
24624  * Bootstrap Graph class
24625 > Prameters
24626  -sm {number} sm 4
24627  -md {number} md 5
24628  @cfg {String} graphtype  bar | vbar | pie
24629  @cfg {number} g_x coodinator | centre x (pie)
24630  @cfg {number} g_y coodinator | centre y (pie)
24631  @cfg {number} g_r radius (pie)
24632  @cfg {number} g_height height of the chart (respected by all elements in the set)
24633  @cfg {number} g_width width of the chart (respected by all elements in the set)
24634  @cfg {Object} title The title of the chart
24635     
24636  -{Array}  values
24637  -opts (object) options for the chart 
24638      o {
24639      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24640      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24641      o vgutter (number)
24642      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.
24643      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24644      o to
24645      o stretch (boolean)
24646      o }
24647  -opts (object) options for the pie
24648      o{
24649      o cut
24650      o startAngle (number)
24651      o endAngle (number)
24652      } 
24653  *
24654  * @constructor
24655  * Create a new Input
24656  * @param {Object} config The config object
24657  */
24658
24659 Roo.bootstrap.Graph = function(config){
24660     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24661     
24662     this.addEvents({
24663         // img events
24664         /**
24665          * @event click
24666          * The img click event for the img.
24667          * @param {Roo.EventObject} e
24668          */
24669         "click" : true
24670     });
24671 };
24672
24673 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24674     
24675     sm: 4,
24676     md: 5,
24677     graphtype: 'bar',
24678     g_height: 250,
24679     g_width: 400,
24680     g_x: 50,
24681     g_y: 50,
24682     g_r: 30,
24683     opts:{
24684         //g_colors: this.colors,
24685         g_type: 'soft',
24686         g_gutter: '20%'
24687
24688     },
24689     title : false,
24690
24691     getAutoCreate : function(){
24692         
24693         var cfg = {
24694             tag: 'div',
24695             html : null
24696         };
24697         
24698         
24699         return  cfg;
24700     },
24701
24702     onRender : function(ct,position){
24703         
24704         
24705         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24706         
24707         if (typeof(Raphael) == 'undefined') {
24708             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24709             return;
24710         }
24711         
24712         this.raphael = Raphael(this.el.dom);
24713         
24714                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24715                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24716                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24717                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24718                 /*
24719                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24720                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24721                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24722                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24723                 
24724                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24725                 r.barchart(330, 10, 300, 220, data1);
24726                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24727                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24728                 */
24729                 
24730                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24731                 // r.barchart(30, 30, 560, 250,  xdata, {
24732                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24733                 //     axis : "0 0 1 1",
24734                 //     axisxlabels :  xdata
24735                 //     //yvalues : cols,
24736                    
24737                 // });
24738 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24739 //        
24740 //        this.load(null,xdata,{
24741 //                axis : "0 0 1 1",
24742 //                axisxlabels :  xdata
24743 //                });
24744
24745     },
24746
24747     load : function(graphtype,xdata,opts)
24748     {
24749         this.raphael.clear();
24750         if(!graphtype) {
24751             graphtype = this.graphtype;
24752         }
24753         if(!opts){
24754             opts = this.opts;
24755         }
24756         var r = this.raphael,
24757             fin = function () {
24758                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24759             },
24760             fout = function () {
24761                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24762             },
24763             pfin = function() {
24764                 this.sector.stop();
24765                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24766
24767                 if (this.label) {
24768                     this.label[0].stop();
24769                     this.label[0].attr({ r: 7.5 });
24770                     this.label[1].attr({ "font-weight": 800 });
24771                 }
24772             },
24773             pfout = function() {
24774                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24775
24776                 if (this.label) {
24777                     this.label[0].animate({ r: 5 }, 500, "bounce");
24778                     this.label[1].attr({ "font-weight": 400 });
24779                 }
24780             };
24781
24782         switch(graphtype){
24783             case 'bar':
24784                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24785                 break;
24786             case 'hbar':
24787                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24788                 break;
24789             case 'pie':
24790 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24791 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24792 //            
24793                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24794                 
24795                 break;
24796
24797         }
24798         
24799         if(this.title){
24800             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24801         }
24802         
24803     },
24804     
24805     setTitle: function(o)
24806     {
24807         this.title = o;
24808     },
24809     
24810     initEvents: function() {
24811         
24812         if(!this.href){
24813             this.el.on('click', this.onClick, this);
24814         }
24815     },
24816     
24817     onClick : function(e)
24818     {
24819         Roo.log('img onclick');
24820         this.fireEvent('click', this, e);
24821     }
24822    
24823 });
24824
24825  
24826 /*
24827  * - LGPL
24828  *
24829  * numberBox
24830  * 
24831  */
24832 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24833
24834 /**
24835  * @class Roo.bootstrap.dash.NumberBox
24836  * @extends Roo.bootstrap.Component
24837  * Bootstrap NumberBox class
24838  * @cfg {String} headline Box headline
24839  * @cfg {String} content Box content
24840  * @cfg {String} icon Box icon
24841  * @cfg {String} footer Footer text
24842  * @cfg {String} fhref Footer href
24843  * 
24844  * @constructor
24845  * Create a new NumberBox
24846  * @param {Object} config The config object
24847  */
24848
24849
24850 Roo.bootstrap.dash.NumberBox = function(config){
24851     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24852     
24853 };
24854
24855 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24856     
24857     headline : '',
24858     content : '',
24859     icon : '',
24860     footer : '',
24861     fhref : '',
24862     ficon : '',
24863     
24864     getAutoCreate : function(){
24865         
24866         var cfg = {
24867             tag : 'div',
24868             cls : 'small-box ',
24869             cn : [
24870                 {
24871                     tag : 'div',
24872                     cls : 'inner',
24873                     cn :[
24874                         {
24875                             tag : 'h3',
24876                             cls : 'roo-headline',
24877                             html : this.headline
24878                         },
24879                         {
24880                             tag : 'p',
24881                             cls : 'roo-content',
24882                             html : this.content
24883                         }
24884                     ]
24885                 }
24886             ]
24887         };
24888         
24889         if(this.icon){
24890             cfg.cn.push({
24891                 tag : 'div',
24892                 cls : 'icon',
24893                 cn :[
24894                     {
24895                         tag : 'i',
24896                         cls : 'ion ' + this.icon
24897                     }
24898                 ]
24899             });
24900         }
24901         
24902         if(this.footer){
24903             var footer = {
24904                 tag : 'a',
24905                 cls : 'small-box-footer',
24906                 href : this.fhref || '#',
24907                 html : this.footer
24908             };
24909             
24910             cfg.cn.push(footer);
24911             
24912         }
24913         
24914         return  cfg;
24915     },
24916
24917     onRender : function(ct,position){
24918         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24919
24920
24921        
24922                 
24923     },
24924
24925     setHeadline: function (value)
24926     {
24927         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24928     },
24929     
24930     setFooter: function (value, href)
24931     {
24932         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24933         
24934         if(href){
24935             this.el.select('a.small-box-footer',true).first().attr('href', href);
24936         }
24937         
24938     },
24939
24940     setContent: function (value)
24941     {
24942         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24943     },
24944
24945     initEvents: function() 
24946     {   
24947         
24948     }
24949     
24950 });
24951
24952  
24953 /*
24954  * - LGPL
24955  *
24956  * TabBox
24957  * 
24958  */
24959 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24960
24961 /**
24962  * @class Roo.bootstrap.dash.TabBox
24963  * @extends Roo.bootstrap.Component
24964  * Bootstrap TabBox class
24965  * @cfg {String} title Title of the TabBox
24966  * @cfg {String} icon Icon of the TabBox
24967  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24968  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24969  * 
24970  * @constructor
24971  * Create a new TabBox
24972  * @param {Object} config The config object
24973  */
24974
24975
24976 Roo.bootstrap.dash.TabBox = function(config){
24977     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24978     this.addEvents({
24979         // raw events
24980         /**
24981          * @event addpane
24982          * When a pane is added
24983          * @param {Roo.bootstrap.dash.TabPane} pane
24984          */
24985         "addpane" : true,
24986         /**
24987          * @event activatepane
24988          * When a pane is activated
24989          * @param {Roo.bootstrap.dash.TabPane} pane
24990          */
24991         "activatepane" : true
24992         
24993          
24994     });
24995     
24996     this.panes = [];
24997 };
24998
24999 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25000
25001     title : '',
25002     icon : false,
25003     showtabs : true,
25004     tabScrollable : false,
25005     
25006     getChildContainer : function()
25007     {
25008         return this.el.select('.tab-content', true).first();
25009     },
25010     
25011     getAutoCreate : function(){
25012         
25013         var header = {
25014             tag: 'li',
25015             cls: 'pull-left header',
25016             html: this.title,
25017             cn : []
25018         };
25019         
25020         if(this.icon){
25021             header.cn.push({
25022                 tag: 'i',
25023                 cls: 'fa ' + this.icon
25024             });
25025         }
25026         
25027         var h = {
25028             tag: 'ul',
25029             cls: 'nav nav-tabs pull-right',
25030             cn: [
25031                 header
25032             ]
25033         };
25034         
25035         if(this.tabScrollable){
25036             h = {
25037                 tag: 'div',
25038                 cls: 'tab-header',
25039                 cn: [
25040                     {
25041                         tag: 'ul',
25042                         cls: 'nav nav-tabs pull-right',
25043                         cn: [
25044                             header
25045                         ]
25046                     }
25047                 ]
25048             };
25049         }
25050         
25051         var cfg = {
25052             tag: 'div',
25053             cls: 'nav-tabs-custom',
25054             cn: [
25055                 h,
25056                 {
25057                     tag: 'div',
25058                     cls: 'tab-content no-padding',
25059                     cn: []
25060                 }
25061             ]
25062         };
25063
25064         return  cfg;
25065     },
25066     initEvents : function()
25067     {
25068         //Roo.log('add add pane handler');
25069         this.on('addpane', this.onAddPane, this);
25070     },
25071      /**
25072      * Updates the box title
25073      * @param {String} html to set the title to.
25074      */
25075     setTitle : function(value)
25076     {
25077         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25078     },
25079     onAddPane : function(pane)
25080     {
25081         this.panes.push(pane);
25082         //Roo.log('addpane');
25083         //Roo.log(pane);
25084         // tabs are rendere left to right..
25085         if(!this.showtabs){
25086             return;
25087         }
25088         
25089         var ctr = this.el.select('.nav-tabs', true).first();
25090          
25091          
25092         var existing = ctr.select('.nav-tab',true);
25093         var qty = existing.getCount();;
25094         
25095         
25096         var tab = ctr.createChild({
25097             tag : 'li',
25098             cls : 'nav-tab' + (qty ? '' : ' active'),
25099             cn : [
25100                 {
25101                     tag : 'a',
25102                     href:'#',
25103                     html : pane.title
25104                 }
25105             ]
25106         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25107         pane.tab = tab;
25108         
25109         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25110         if (!qty) {
25111             pane.el.addClass('active');
25112         }
25113         
25114                 
25115     },
25116     onTabClick : function(ev,un,ob,pane)
25117     {
25118         //Roo.log('tab - prev default');
25119         ev.preventDefault();
25120         
25121         
25122         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25123         pane.tab.addClass('active');
25124         //Roo.log(pane.title);
25125         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25126         // technically we should have a deactivate event.. but maybe add later.
25127         // and it should not de-activate the selected tab...
25128         this.fireEvent('activatepane', pane);
25129         pane.el.addClass('active');
25130         pane.fireEvent('activate');
25131         
25132         
25133     },
25134     
25135     getActivePane : function()
25136     {
25137         var r = false;
25138         Roo.each(this.panes, function(p) {
25139             if(p.el.hasClass('active')){
25140                 r = p;
25141                 return false;
25142             }
25143             
25144             return;
25145         });
25146         
25147         return r;
25148     }
25149     
25150     
25151 });
25152
25153  
25154 /*
25155  * - LGPL
25156  *
25157  * Tab pane
25158  * 
25159  */
25160 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25161 /**
25162  * @class Roo.bootstrap.TabPane
25163  * @extends Roo.bootstrap.Component
25164  * Bootstrap TabPane class
25165  * @cfg {Boolean} active (false | true) Default false
25166  * @cfg {String} title title of panel
25167
25168  * 
25169  * @constructor
25170  * Create a new TabPane
25171  * @param {Object} config The config object
25172  */
25173
25174 Roo.bootstrap.dash.TabPane = function(config){
25175     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25176     
25177     this.addEvents({
25178         // raw events
25179         /**
25180          * @event activate
25181          * When a pane is activated
25182          * @param {Roo.bootstrap.dash.TabPane} pane
25183          */
25184         "activate" : true
25185          
25186     });
25187 };
25188
25189 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25190     
25191     active : false,
25192     title : '',
25193     
25194     // the tabBox that this is attached to.
25195     tab : false,
25196      
25197     getAutoCreate : function() 
25198     {
25199         var cfg = {
25200             tag: 'div',
25201             cls: 'tab-pane'
25202         };
25203         
25204         if(this.active){
25205             cfg.cls += ' active';
25206         }
25207         
25208         return cfg;
25209     },
25210     initEvents  : function()
25211     {
25212         //Roo.log('trigger add pane handler');
25213         this.parent().fireEvent('addpane', this)
25214     },
25215     
25216      /**
25217      * Updates the tab title 
25218      * @param {String} html to set the title to.
25219      */
25220     setTitle: function(str)
25221     {
25222         if (!this.tab) {
25223             return;
25224         }
25225         this.title = str;
25226         this.tab.select('a', true).first().dom.innerHTML = str;
25227         
25228     }
25229     
25230     
25231     
25232 });
25233
25234  
25235
25236
25237  /*
25238  * - LGPL
25239  *
25240  * menu
25241  * 
25242  */
25243 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25244
25245 /**
25246  * @class Roo.bootstrap.menu.Menu
25247  * @extends Roo.bootstrap.Component
25248  * Bootstrap Menu class - container for Menu
25249  * @cfg {String} html Text of the menu
25250  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25251  * @cfg {String} icon Font awesome icon
25252  * @cfg {String} pos Menu align to (top | bottom) default bottom
25253  * 
25254  * 
25255  * @constructor
25256  * Create a new Menu
25257  * @param {Object} config The config object
25258  */
25259
25260
25261 Roo.bootstrap.menu.Menu = function(config){
25262     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25263     
25264     this.addEvents({
25265         /**
25266          * @event beforeshow
25267          * Fires before this menu is displayed
25268          * @param {Roo.bootstrap.menu.Menu} this
25269          */
25270         beforeshow : true,
25271         /**
25272          * @event beforehide
25273          * Fires before this menu is hidden
25274          * @param {Roo.bootstrap.menu.Menu} this
25275          */
25276         beforehide : true,
25277         /**
25278          * @event show
25279          * Fires after this menu is displayed
25280          * @param {Roo.bootstrap.menu.Menu} this
25281          */
25282         show : true,
25283         /**
25284          * @event hide
25285          * Fires after this menu is hidden
25286          * @param {Roo.bootstrap.menu.Menu} this
25287          */
25288         hide : true,
25289         /**
25290          * @event click
25291          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25292          * @param {Roo.bootstrap.menu.Menu} this
25293          * @param {Roo.EventObject} e
25294          */
25295         click : true
25296     });
25297     
25298 };
25299
25300 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25301     
25302     submenu : false,
25303     html : '',
25304     weight : 'default',
25305     icon : false,
25306     pos : 'bottom',
25307     
25308     
25309     getChildContainer : function() {
25310         if(this.isSubMenu){
25311             return this.el;
25312         }
25313         
25314         return this.el.select('ul.dropdown-menu', true).first();  
25315     },
25316     
25317     getAutoCreate : function()
25318     {
25319         var text = [
25320             {
25321                 tag : 'span',
25322                 cls : 'roo-menu-text',
25323                 html : this.html
25324             }
25325         ];
25326         
25327         if(this.icon){
25328             text.unshift({
25329                 tag : 'i',
25330                 cls : 'fa ' + this.icon
25331             })
25332         }
25333         
25334         
25335         var cfg = {
25336             tag : 'div',
25337             cls : 'btn-group',
25338             cn : [
25339                 {
25340                     tag : 'button',
25341                     cls : 'dropdown-button btn btn-' + this.weight,
25342                     cn : text
25343                 },
25344                 {
25345                     tag : 'button',
25346                     cls : 'dropdown-toggle btn btn-' + this.weight,
25347                     cn : [
25348                         {
25349                             tag : 'span',
25350                             cls : 'caret'
25351                         }
25352                     ]
25353                 },
25354                 {
25355                     tag : 'ul',
25356                     cls : 'dropdown-menu'
25357                 }
25358             ]
25359             
25360         };
25361         
25362         if(this.pos == 'top'){
25363             cfg.cls += ' dropup';
25364         }
25365         
25366         if(this.isSubMenu){
25367             cfg = {
25368                 tag : 'ul',
25369                 cls : 'dropdown-menu'
25370             }
25371         }
25372         
25373         return cfg;
25374     },
25375     
25376     onRender : function(ct, position)
25377     {
25378         this.isSubMenu = ct.hasClass('dropdown-submenu');
25379         
25380         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25381     },
25382     
25383     initEvents : function() 
25384     {
25385         if(this.isSubMenu){
25386             return;
25387         }
25388         
25389         this.hidden = true;
25390         
25391         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25392         this.triggerEl.on('click', this.onTriggerPress, this);
25393         
25394         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25395         this.buttonEl.on('click', this.onClick, this);
25396         
25397     },
25398     
25399     list : function()
25400     {
25401         if(this.isSubMenu){
25402             return this.el;
25403         }
25404         
25405         return this.el.select('ul.dropdown-menu', true).first();
25406     },
25407     
25408     onClick : function(e)
25409     {
25410         this.fireEvent("click", this, e);
25411     },
25412     
25413     onTriggerPress  : function(e)
25414     {   
25415         if (this.isVisible()) {
25416             this.hide();
25417         } else {
25418             this.show();
25419         }
25420     },
25421     
25422     isVisible : function(){
25423         return !this.hidden;
25424     },
25425     
25426     show : function()
25427     {
25428         this.fireEvent("beforeshow", this);
25429         
25430         this.hidden = false;
25431         this.el.addClass('open');
25432         
25433         Roo.get(document).on("mouseup", this.onMouseUp, this);
25434         
25435         this.fireEvent("show", this);
25436         
25437         
25438     },
25439     
25440     hide : function()
25441     {
25442         this.fireEvent("beforehide", this);
25443         
25444         this.hidden = true;
25445         this.el.removeClass('open');
25446         
25447         Roo.get(document).un("mouseup", this.onMouseUp);
25448         
25449         this.fireEvent("hide", this);
25450     },
25451     
25452     onMouseUp : function()
25453     {
25454         this.hide();
25455     }
25456     
25457 });
25458
25459  
25460  /*
25461  * - LGPL
25462  *
25463  * menu item
25464  * 
25465  */
25466 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25467
25468 /**
25469  * @class Roo.bootstrap.menu.Item
25470  * @extends Roo.bootstrap.Component
25471  * Bootstrap MenuItem class
25472  * @cfg {Boolean} submenu (true | false) default false
25473  * @cfg {String} html text of the item
25474  * @cfg {String} href the link
25475  * @cfg {Boolean} disable (true | false) default false
25476  * @cfg {Boolean} preventDefault (true | false) default true
25477  * @cfg {String} icon Font awesome icon
25478  * @cfg {String} pos Submenu align to (left | right) default right 
25479  * 
25480  * 
25481  * @constructor
25482  * Create a new Item
25483  * @param {Object} config The config object
25484  */
25485
25486
25487 Roo.bootstrap.menu.Item = function(config){
25488     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25489     this.addEvents({
25490         /**
25491          * @event mouseover
25492          * Fires when the mouse is hovering over this menu
25493          * @param {Roo.bootstrap.menu.Item} this
25494          * @param {Roo.EventObject} e
25495          */
25496         mouseover : true,
25497         /**
25498          * @event mouseout
25499          * Fires when the mouse exits this menu
25500          * @param {Roo.bootstrap.menu.Item} this
25501          * @param {Roo.EventObject} e
25502          */
25503         mouseout : true,
25504         // raw events
25505         /**
25506          * @event click
25507          * The raw click event for the entire grid.
25508          * @param {Roo.EventObject} e
25509          */
25510         click : true
25511     });
25512 };
25513
25514 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25515     
25516     submenu : false,
25517     href : '',
25518     html : '',
25519     preventDefault: true,
25520     disable : false,
25521     icon : false,
25522     pos : 'right',
25523     
25524     getAutoCreate : function()
25525     {
25526         var text = [
25527             {
25528                 tag : 'span',
25529                 cls : 'roo-menu-item-text',
25530                 html : this.html
25531             }
25532         ];
25533         
25534         if(this.icon){
25535             text.unshift({
25536                 tag : 'i',
25537                 cls : 'fa ' + this.icon
25538             })
25539         }
25540         
25541         var cfg = {
25542             tag : 'li',
25543             cn : [
25544                 {
25545                     tag : 'a',
25546                     href : this.href || '#',
25547                     cn : text
25548                 }
25549             ]
25550         };
25551         
25552         if(this.disable){
25553             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25554         }
25555         
25556         if(this.submenu){
25557             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25558             
25559             if(this.pos == 'left'){
25560                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25561             }
25562         }
25563         
25564         return cfg;
25565     },
25566     
25567     initEvents : function() 
25568     {
25569         this.el.on('mouseover', this.onMouseOver, this);
25570         this.el.on('mouseout', this.onMouseOut, this);
25571         
25572         this.el.select('a', true).first().on('click', this.onClick, this);
25573         
25574     },
25575     
25576     onClick : function(e)
25577     {
25578         if(this.preventDefault){
25579             e.preventDefault();
25580         }
25581         
25582         this.fireEvent("click", this, e);
25583     },
25584     
25585     onMouseOver : function(e)
25586     {
25587         if(this.submenu && this.pos == 'left'){
25588             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25589         }
25590         
25591         this.fireEvent("mouseover", this, e);
25592     },
25593     
25594     onMouseOut : function(e)
25595     {
25596         this.fireEvent("mouseout", this, e);
25597     }
25598 });
25599
25600  
25601
25602  /*
25603  * - LGPL
25604  *
25605  * menu separator
25606  * 
25607  */
25608 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25609
25610 /**
25611  * @class Roo.bootstrap.menu.Separator
25612  * @extends Roo.bootstrap.Component
25613  * Bootstrap Separator class
25614  * 
25615  * @constructor
25616  * Create a new Separator
25617  * @param {Object} config The config object
25618  */
25619
25620
25621 Roo.bootstrap.menu.Separator = function(config){
25622     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25623 };
25624
25625 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25626     
25627     getAutoCreate : function(){
25628         var cfg = {
25629             tag : 'li',
25630             cls: 'divider'
25631         };
25632         
25633         return cfg;
25634     }
25635    
25636 });
25637
25638  
25639
25640  /*
25641  * - LGPL
25642  *
25643  * Tooltip
25644  * 
25645  */
25646
25647 /**
25648  * @class Roo.bootstrap.Tooltip
25649  * Bootstrap Tooltip class
25650  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25651  * to determine which dom element triggers the tooltip.
25652  * 
25653  * It needs to add support for additional attributes like tooltip-position
25654  * 
25655  * @constructor
25656  * Create a new Toolti
25657  * @param {Object} config The config object
25658  */
25659
25660 Roo.bootstrap.Tooltip = function(config){
25661     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25662     
25663     this.alignment = Roo.bootstrap.Tooltip.alignment;
25664     
25665     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25666         this.alignment = config.alignment;
25667     }
25668     
25669 };
25670
25671 Roo.apply(Roo.bootstrap.Tooltip, {
25672     /**
25673      * @function init initialize tooltip monitoring.
25674      * @static
25675      */
25676     currentEl : false,
25677     currentTip : false,
25678     currentRegion : false,
25679     
25680     //  init : delay?
25681     
25682     init : function()
25683     {
25684         Roo.get(document).on('mouseover', this.enter ,this);
25685         Roo.get(document).on('mouseout', this.leave, this);
25686          
25687         
25688         this.currentTip = new Roo.bootstrap.Tooltip();
25689     },
25690     
25691     enter : function(ev)
25692     {
25693         var dom = ev.getTarget();
25694         
25695         //Roo.log(['enter',dom]);
25696         var el = Roo.fly(dom);
25697         if (this.currentEl) {
25698             //Roo.log(dom);
25699             //Roo.log(this.currentEl);
25700             //Roo.log(this.currentEl.contains(dom));
25701             if (this.currentEl == el) {
25702                 return;
25703             }
25704             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25705                 return;
25706             }
25707
25708         }
25709         
25710         if (this.currentTip.el) {
25711             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25712         }    
25713         //Roo.log(ev);
25714         
25715         if(!el || el.dom == document){
25716             return;
25717         }
25718         
25719         var bindEl = el;
25720         
25721         // you can not look for children, as if el is the body.. then everythign is the child..
25722         if (!el.attr('tooltip')) { //
25723             if (!el.select("[tooltip]").elements.length) {
25724                 return;
25725             }
25726             // is the mouse over this child...?
25727             bindEl = el.select("[tooltip]").first();
25728             var xy = ev.getXY();
25729             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25730                 //Roo.log("not in region.");
25731                 return;
25732             }
25733             //Roo.log("child element over..");
25734             
25735         }
25736         this.currentEl = bindEl;
25737         this.currentTip.bind(bindEl);
25738         this.currentRegion = Roo.lib.Region.getRegion(dom);
25739         this.currentTip.enter();
25740         
25741     },
25742     leave : function(ev)
25743     {
25744         var dom = ev.getTarget();
25745         //Roo.log(['leave',dom]);
25746         if (!this.currentEl) {
25747             return;
25748         }
25749         
25750         
25751         if (dom != this.currentEl.dom) {
25752             return;
25753         }
25754         var xy = ev.getXY();
25755         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25756             return;
25757         }
25758         // only activate leave if mouse cursor is outside... bounding box..
25759         
25760         
25761         
25762         
25763         if (this.currentTip) {
25764             this.currentTip.leave();
25765         }
25766         //Roo.log('clear currentEl');
25767         this.currentEl = false;
25768         
25769         
25770     },
25771     alignment : {
25772         'left' : ['r-l', [-2,0], 'right'],
25773         'right' : ['l-r', [2,0], 'left'],
25774         'bottom' : ['t-b', [0,2], 'top'],
25775         'top' : [ 'b-t', [0,-2], 'bottom']
25776     }
25777     
25778 });
25779
25780
25781 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25782     
25783     
25784     bindEl : false,
25785     
25786     delay : null, // can be { show : 300 , hide: 500}
25787     
25788     timeout : null,
25789     
25790     hoverState : null, //???
25791     
25792     placement : 'bottom', 
25793     
25794     alignment : false,
25795     
25796     getAutoCreate : function(){
25797     
25798         var cfg = {
25799            cls : 'tooltip',
25800            role : 'tooltip',
25801            cn : [
25802                 {
25803                     cls : 'tooltip-arrow'
25804                 },
25805                 {
25806                     cls : 'tooltip-inner'
25807                 }
25808            ]
25809         };
25810         
25811         return cfg;
25812     },
25813     bind : function(el)
25814     {
25815         this.bindEl = el;
25816     },
25817       
25818     
25819     enter : function () {
25820        
25821         if (this.timeout != null) {
25822             clearTimeout(this.timeout);
25823         }
25824         
25825         this.hoverState = 'in';
25826          //Roo.log("enter - show");
25827         if (!this.delay || !this.delay.show) {
25828             this.show();
25829             return;
25830         }
25831         var _t = this;
25832         this.timeout = setTimeout(function () {
25833             if (_t.hoverState == 'in') {
25834                 _t.show();
25835             }
25836         }, this.delay.show);
25837     },
25838     leave : function()
25839     {
25840         clearTimeout(this.timeout);
25841     
25842         this.hoverState = 'out';
25843          if (!this.delay || !this.delay.hide) {
25844             this.hide();
25845             return;
25846         }
25847        
25848         var _t = this;
25849         this.timeout = setTimeout(function () {
25850             //Roo.log("leave - timeout");
25851             
25852             if (_t.hoverState == 'out') {
25853                 _t.hide();
25854                 Roo.bootstrap.Tooltip.currentEl = false;
25855             }
25856         }, delay);
25857     },
25858     
25859     show : function (msg)
25860     {
25861         if (!this.el) {
25862             this.render(document.body);
25863         }
25864         // set content.
25865         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25866         
25867         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25868         
25869         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25870         
25871         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25872         
25873         var placement = typeof this.placement == 'function' ?
25874             this.placement.call(this, this.el, on_el) :
25875             this.placement;
25876             
25877         var autoToken = /\s?auto?\s?/i;
25878         var autoPlace = autoToken.test(placement);
25879         if (autoPlace) {
25880             placement = placement.replace(autoToken, '') || 'top';
25881         }
25882         
25883         //this.el.detach()
25884         //this.el.setXY([0,0]);
25885         this.el.show();
25886         //this.el.dom.style.display='block';
25887         
25888         //this.el.appendTo(on_el);
25889         
25890         var p = this.getPosition();
25891         var box = this.el.getBox();
25892         
25893         if (autoPlace) {
25894             // fixme..
25895         }
25896         
25897         var align = this.alignment[placement];
25898         
25899         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25900         
25901         if(placement == 'top' || placement == 'bottom'){
25902             if(xy[0] < 0){
25903                 placement = 'right';
25904             }
25905             
25906             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25907                 placement = 'left';
25908             }
25909             
25910             var scroll = Roo.select('body', true).first().getScroll();
25911             
25912             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25913                 placement = 'top';
25914             }
25915             
25916         }
25917         
25918         this.el.alignTo(this.bindEl, align[0],align[1]);
25919         //var arrow = this.el.select('.arrow',true).first();
25920         //arrow.set(align[2], 
25921         
25922         this.el.addClass(placement);
25923         
25924         this.el.addClass('in fade');
25925         
25926         this.hoverState = null;
25927         
25928         if (this.el.hasClass('fade')) {
25929             // fade it?
25930         }
25931         
25932     },
25933     hide : function()
25934     {
25935          
25936         if (!this.el) {
25937             return;
25938         }
25939         //this.el.setXY([0,0]);
25940         this.el.removeClass('in');
25941         //this.el.hide();
25942         
25943     }
25944     
25945 });
25946  
25947
25948  /*
25949  * - LGPL
25950  *
25951  * Location Picker
25952  * 
25953  */
25954
25955 /**
25956  * @class Roo.bootstrap.LocationPicker
25957  * @extends Roo.bootstrap.Component
25958  * Bootstrap LocationPicker class
25959  * @cfg {Number} latitude Position when init default 0
25960  * @cfg {Number} longitude Position when init default 0
25961  * @cfg {Number} zoom default 15
25962  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25963  * @cfg {Boolean} mapTypeControl default false
25964  * @cfg {Boolean} disableDoubleClickZoom default false
25965  * @cfg {Boolean} scrollwheel default true
25966  * @cfg {Boolean} streetViewControl default false
25967  * @cfg {Number} radius default 0
25968  * @cfg {String} locationName
25969  * @cfg {Boolean} draggable default true
25970  * @cfg {Boolean} enableAutocomplete default false
25971  * @cfg {Boolean} enableReverseGeocode default true
25972  * @cfg {String} markerTitle
25973  * 
25974  * @constructor
25975  * Create a new LocationPicker
25976  * @param {Object} config The config object
25977  */
25978
25979
25980 Roo.bootstrap.LocationPicker = function(config){
25981     
25982     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25983     
25984     this.addEvents({
25985         /**
25986          * @event initial
25987          * Fires when the picker initialized.
25988          * @param {Roo.bootstrap.LocationPicker} this
25989          * @param {Google Location} location
25990          */
25991         initial : true,
25992         /**
25993          * @event positionchanged
25994          * Fires when the picker position changed.
25995          * @param {Roo.bootstrap.LocationPicker} this
25996          * @param {Google Location} location
25997          */
25998         positionchanged : true,
25999         /**
26000          * @event resize
26001          * Fires when the map resize.
26002          * @param {Roo.bootstrap.LocationPicker} this
26003          */
26004         resize : true,
26005         /**
26006          * @event show
26007          * Fires when the map show.
26008          * @param {Roo.bootstrap.LocationPicker} this
26009          */
26010         show : true,
26011         /**
26012          * @event hide
26013          * Fires when the map hide.
26014          * @param {Roo.bootstrap.LocationPicker} this
26015          */
26016         hide : true,
26017         /**
26018          * @event mapClick
26019          * Fires when click the map.
26020          * @param {Roo.bootstrap.LocationPicker} this
26021          * @param {Map event} e
26022          */
26023         mapClick : true,
26024         /**
26025          * @event mapRightClick
26026          * Fires when right click the map.
26027          * @param {Roo.bootstrap.LocationPicker} this
26028          * @param {Map event} e
26029          */
26030         mapRightClick : true,
26031         /**
26032          * @event markerClick
26033          * Fires when click the marker.
26034          * @param {Roo.bootstrap.LocationPicker} this
26035          * @param {Map event} e
26036          */
26037         markerClick : true,
26038         /**
26039          * @event markerRightClick
26040          * Fires when right click the marker.
26041          * @param {Roo.bootstrap.LocationPicker} this
26042          * @param {Map event} e
26043          */
26044         markerRightClick : true,
26045         /**
26046          * @event OverlayViewDraw
26047          * Fires when OverlayView Draw
26048          * @param {Roo.bootstrap.LocationPicker} this
26049          */
26050         OverlayViewDraw : true,
26051         /**
26052          * @event OverlayViewOnAdd
26053          * Fires when OverlayView Draw
26054          * @param {Roo.bootstrap.LocationPicker} this
26055          */
26056         OverlayViewOnAdd : true,
26057         /**
26058          * @event OverlayViewOnRemove
26059          * Fires when OverlayView Draw
26060          * @param {Roo.bootstrap.LocationPicker} this
26061          */
26062         OverlayViewOnRemove : true,
26063         /**
26064          * @event OverlayViewShow
26065          * Fires when OverlayView Draw
26066          * @param {Roo.bootstrap.LocationPicker} this
26067          * @param {Pixel} cpx
26068          */
26069         OverlayViewShow : true,
26070         /**
26071          * @event OverlayViewHide
26072          * Fires when OverlayView Draw
26073          * @param {Roo.bootstrap.LocationPicker} this
26074          */
26075         OverlayViewHide : true,
26076         /**
26077          * @event loadexception
26078          * Fires when load google lib failed.
26079          * @param {Roo.bootstrap.LocationPicker} this
26080          */
26081         loadexception : true
26082     });
26083         
26084 };
26085
26086 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26087     
26088     gMapContext: false,
26089     
26090     latitude: 0,
26091     longitude: 0,
26092     zoom: 15,
26093     mapTypeId: false,
26094     mapTypeControl: false,
26095     disableDoubleClickZoom: false,
26096     scrollwheel: true,
26097     streetViewControl: false,
26098     radius: 0,
26099     locationName: '',
26100     draggable: true,
26101     enableAutocomplete: false,
26102     enableReverseGeocode: true,
26103     markerTitle: '',
26104     
26105     getAutoCreate: function()
26106     {
26107
26108         var cfg = {
26109             tag: 'div',
26110             cls: 'roo-location-picker'
26111         };
26112         
26113         return cfg
26114     },
26115     
26116     initEvents: function(ct, position)
26117     {       
26118         if(!this.el.getWidth() || this.isApplied()){
26119             return;
26120         }
26121         
26122         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26123         
26124         this.initial();
26125     },
26126     
26127     initial: function()
26128     {
26129         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26130             this.fireEvent('loadexception', this);
26131             return;
26132         }
26133         
26134         if(!this.mapTypeId){
26135             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26136         }
26137         
26138         this.gMapContext = this.GMapContext();
26139         
26140         this.initOverlayView();
26141         
26142         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26143         
26144         var _this = this;
26145                 
26146         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26147             _this.setPosition(_this.gMapContext.marker.position);
26148         });
26149         
26150         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26151             _this.fireEvent('mapClick', this, event);
26152             
26153         });
26154
26155         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26156             _this.fireEvent('mapRightClick', this, event);
26157             
26158         });
26159         
26160         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26161             _this.fireEvent('markerClick', this, event);
26162             
26163         });
26164
26165         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26166             _this.fireEvent('markerRightClick', this, event);
26167             
26168         });
26169         
26170         this.setPosition(this.gMapContext.location);
26171         
26172         this.fireEvent('initial', this, this.gMapContext.location);
26173     },
26174     
26175     initOverlayView: function()
26176     {
26177         var _this = this;
26178         
26179         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26180             
26181             draw: function()
26182             {
26183                 _this.fireEvent('OverlayViewDraw', _this);
26184             },
26185             
26186             onAdd: function()
26187             {
26188                 _this.fireEvent('OverlayViewOnAdd', _this);
26189             },
26190             
26191             onRemove: function()
26192             {
26193                 _this.fireEvent('OverlayViewOnRemove', _this);
26194             },
26195             
26196             show: function(cpx)
26197             {
26198                 _this.fireEvent('OverlayViewShow', _this, cpx);
26199             },
26200             
26201             hide: function()
26202             {
26203                 _this.fireEvent('OverlayViewHide', _this);
26204             }
26205             
26206         });
26207     },
26208     
26209     fromLatLngToContainerPixel: function(event)
26210     {
26211         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26212     },
26213     
26214     isApplied: function() 
26215     {
26216         return this.getGmapContext() == false ? false : true;
26217     },
26218     
26219     getGmapContext: function() 
26220     {
26221         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26222     },
26223     
26224     GMapContext: function() 
26225     {
26226         var position = new google.maps.LatLng(this.latitude, this.longitude);
26227         
26228         var _map = new google.maps.Map(this.el.dom, {
26229             center: position,
26230             zoom: this.zoom,
26231             mapTypeId: this.mapTypeId,
26232             mapTypeControl: this.mapTypeControl,
26233             disableDoubleClickZoom: this.disableDoubleClickZoom,
26234             scrollwheel: this.scrollwheel,
26235             streetViewControl: this.streetViewControl,
26236             locationName: this.locationName,
26237             draggable: this.draggable,
26238             enableAutocomplete: this.enableAutocomplete,
26239             enableReverseGeocode: this.enableReverseGeocode
26240         });
26241         
26242         var _marker = new google.maps.Marker({
26243             position: position,
26244             map: _map,
26245             title: this.markerTitle,
26246             draggable: this.draggable
26247         });
26248         
26249         return {
26250             map: _map,
26251             marker: _marker,
26252             circle: null,
26253             location: position,
26254             radius: this.radius,
26255             locationName: this.locationName,
26256             addressComponents: {
26257                 formatted_address: null,
26258                 addressLine1: null,
26259                 addressLine2: null,
26260                 streetName: null,
26261                 streetNumber: null,
26262                 city: null,
26263                 district: null,
26264                 state: null,
26265                 stateOrProvince: null
26266             },
26267             settings: this,
26268             domContainer: this.el.dom,
26269             geodecoder: new google.maps.Geocoder()
26270         };
26271     },
26272     
26273     drawCircle: function(center, radius, options) 
26274     {
26275         if (this.gMapContext.circle != null) {
26276             this.gMapContext.circle.setMap(null);
26277         }
26278         if (radius > 0) {
26279             radius *= 1;
26280             options = Roo.apply({}, options, {
26281                 strokeColor: "#0000FF",
26282                 strokeOpacity: .35,
26283                 strokeWeight: 2,
26284                 fillColor: "#0000FF",
26285                 fillOpacity: .2
26286             });
26287             
26288             options.map = this.gMapContext.map;
26289             options.radius = radius;
26290             options.center = center;
26291             this.gMapContext.circle = new google.maps.Circle(options);
26292             return this.gMapContext.circle;
26293         }
26294         
26295         return null;
26296     },
26297     
26298     setPosition: function(location) 
26299     {
26300         this.gMapContext.location = location;
26301         this.gMapContext.marker.setPosition(location);
26302         this.gMapContext.map.panTo(location);
26303         this.drawCircle(location, this.gMapContext.radius, {});
26304         
26305         var _this = this;
26306         
26307         if (this.gMapContext.settings.enableReverseGeocode) {
26308             this.gMapContext.geodecoder.geocode({
26309                 latLng: this.gMapContext.location
26310             }, function(results, status) {
26311                 
26312                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26313                     _this.gMapContext.locationName = results[0].formatted_address;
26314                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26315                     
26316                     _this.fireEvent('positionchanged', this, location);
26317                 }
26318             });
26319             
26320             return;
26321         }
26322         
26323         this.fireEvent('positionchanged', this, location);
26324     },
26325     
26326     resize: function()
26327     {
26328         google.maps.event.trigger(this.gMapContext.map, "resize");
26329         
26330         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26331         
26332         this.fireEvent('resize', this);
26333     },
26334     
26335     setPositionByLatLng: function(latitude, longitude)
26336     {
26337         this.setPosition(new google.maps.LatLng(latitude, longitude));
26338     },
26339     
26340     getCurrentPosition: function() 
26341     {
26342         return {
26343             latitude: this.gMapContext.location.lat(),
26344             longitude: this.gMapContext.location.lng()
26345         };
26346     },
26347     
26348     getAddressName: function() 
26349     {
26350         return this.gMapContext.locationName;
26351     },
26352     
26353     getAddressComponents: function() 
26354     {
26355         return this.gMapContext.addressComponents;
26356     },
26357     
26358     address_component_from_google_geocode: function(address_components) 
26359     {
26360         var result = {};
26361         
26362         for (var i = 0; i < address_components.length; i++) {
26363             var component = address_components[i];
26364             if (component.types.indexOf("postal_code") >= 0) {
26365                 result.postalCode = component.short_name;
26366             } else if (component.types.indexOf("street_number") >= 0) {
26367                 result.streetNumber = component.short_name;
26368             } else if (component.types.indexOf("route") >= 0) {
26369                 result.streetName = component.short_name;
26370             } else if (component.types.indexOf("neighborhood") >= 0) {
26371                 result.city = component.short_name;
26372             } else if (component.types.indexOf("locality") >= 0) {
26373                 result.city = component.short_name;
26374             } else if (component.types.indexOf("sublocality") >= 0) {
26375                 result.district = component.short_name;
26376             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26377                 result.stateOrProvince = component.short_name;
26378             } else if (component.types.indexOf("country") >= 0) {
26379                 result.country = component.short_name;
26380             }
26381         }
26382         
26383         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26384         result.addressLine2 = "";
26385         return result;
26386     },
26387     
26388     setZoomLevel: function(zoom)
26389     {
26390         this.gMapContext.map.setZoom(zoom);
26391     },
26392     
26393     show: function()
26394     {
26395         if(!this.el){
26396             return;
26397         }
26398         
26399         this.el.show();
26400         
26401         this.resize();
26402         
26403         this.fireEvent('show', this);
26404     },
26405     
26406     hide: function()
26407     {
26408         if(!this.el){
26409             return;
26410         }
26411         
26412         this.el.hide();
26413         
26414         this.fireEvent('hide', this);
26415     }
26416     
26417 });
26418
26419 Roo.apply(Roo.bootstrap.LocationPicker, {
26420     
26421     OverlayView : function(map, options)
26422     {
26423         options = options || {};
26424         
26425         this.setMap(map);
26426     }
26427     
26428     
26429 });/*
26430  * - LGPL
26431  *
26432  * Alert
26433  * 
26434  */
26435
26436 /**
26437  * @class Roo.bootstrap.Alert
26438  * @extends Roo.bootstrap.Component
26439  * Bootstrap Alert class
26440  * @cfg {String} title The title of alert
26441  * @cfg {String} html The content of alert
26442  * @cfg {String} weight (  success | info | warning | danger )
26443  * @cfg {String} faicon font-awesomeicon
26444  * 
26445  * @constructor
26446  * Create a new alert
26447  * @param {Object} config The config object
26448  */
26449
26450
26451 Roo.bootstrap.Alert = function(config){
26452     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26453     
26454 };
26455
26456 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26457     
26458     title: '',
26459     html: '',
26460     weight: false,
26461     faicon: false,
26462     
26463     getAutoCreate : function()
26464     {
26465         
26466         var cfg = {
26467             tag : 'div',
26468             cls : 'alert',
26469             cn : [
26470                 {
26471                     tag : 'i',
26472                     cls : 'roo-alert-icon'
26473                     
26474                 },
26475                 {
26476                     tag : 'b',
26477                     cls : 'roo-alert-title',
26478                     html : this.title
26479                 },
26480                 {
26481                     tag : 'span',
26482                     cls : 'roo-alert-text',
26483                     html : this.html
26484                 }
26485             ]
26486         };
26487         
26488         if(this.faicon){
26489             cfg.cn[0].cls += ' fa ' + this.faicon;
26490         }
26491         
26492         if(this.weight){
26493             cfg.cls += ' alert-' + this.weight;
26494         }
26495         
26496         return cfg;
26497     },
26498     
26499     initEvents: function() 
26500     {
26501         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26502     },
26503     
26504     setTitle : function(str)
26505     {
26506         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26507     },
26508     
26509     setText : function(str)
26510     {
26511         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26512     },
26513     
26514     setWeight : function(weight)
26515     {
26516         if(this.weight){
26517             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26518         }
26519         
26520         this.weight = weight;
26521         
26522         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26523     },
26524     
26525     setIcon : function(icon)
26526     {
26527         if(this.faicon){
26528             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26529         }
26530         
26531         this.faicon = icon;
26532         
26533         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26534     },
26535     
26536     hide: function() 
26537     {
26538         this.el.hide();   
26539     },
26540     
26541     show: function() 
26542     {  
26543         this.el.show();   
26544     }
26545     
26546 });
26547
26548  
26549 /*
26550 * Licence: LGPL
26551 */
26552
26553 /**
26554  * @class Roo.bootstrap.UploadCropbox
26555  * @extends Roo.bootstrap.Component
26556  * Bootstrap UploadCropbox class
26557  * @cfg {String} emptyText show when image has been loaded
26558  * @cfg {String} rotateNotify show when image too small to rotate
26559  * @cfg {Number} errorTimeout default 3000
26560  * @cfg {Number} minWidth default 300
26561  * @cfg {Number} minHeight default 300
26562  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26563  * @cfg {Boolean} isDocument (true|false) default false
26564  * @cfg {String} url action url
26565  * @cfg {String} paramName default 'imageUpload'
26566  * @cfg {String} method default POST
26567  * @cfg {Boolean} loadMask (true|false) default true
26568  * @cfg {Boolean} loadingText default 'Loading...'
26569  * 
26570  * @constructor
26571  * Create a new UploadCropbox
26572  * @param {Object} config The config object
26573  */
26574
26575 Roo.bootstrap.UploadCropbox = function(config){
26576     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26577     
26578     this.addEvents({
26579         /**
26580          * @event beforeselectfile
26581          * Fire before select file
26582          * @param {Roo.bootstrap.UploadCropbox} this
26583          */
26584         "beforeselectfile" : true,
26585         /**
26586          * @event initial
26587          * Fire after initEvent
26588          * @param {Roo.bootstrap.UploadCropbox} this
26589          */
26590         "initial" : true,
26591         /**
26592          * @event crop
26593          * Fire after initEvent
26594          * @param {Roo.bootstrap.UploadCropbox} this
26595          * @param {String} data
26596          */
26597         "crop" : true,
26598         /**
26599          * @event prepare
26600          * Fire when preparing the file data
26601          * @param {Roo.bootstrap.UploadCropbox} this
26602          * @param {Object} file
26603          */
26604         "prepare" : true,
26605         /**
26606          * @event exception
26607          * Fire when get exception
26608          * @param {Roo.bootstrap.UploadCropbox} this
26609          * @param {XMLHttpRequest} xhr
26610          */
26611         "exception" : true,
26612         /**
26613          * @event beforeloadcanvas
26614          * Fire before load the canvas
26615          * @param {Roo.bootstrap.UploadCropbox} this
26616          * @param {String} src
26617          */
26618         "beforeloadcanvas" : true,
26619         /**
26620          * @event trash
26621          * Fire when trash image
26622          * @param {Roo.bootstrap.UploadCropbox} this
26623          */
26624         "trash" : true,
26625         /**
26626          * @event download
26627          * Fire when download the image
26628          * @param {Roo.bootstrap.UploadCropbox} this
26629          */
26630         "download" : true,
26631         /**
26632          * @event footerbuttonclick
26633          * Fire when footerbuttonclick
26634          * @param {Roo.bootstrap.UploadCropbox} this
26635          * @param {String} type
26636          */
26637         "footerbuttonclick" : true,
26638         /**
26639          * @event resize
26640          * Fire when resize
26641          * @param {Roo.bootstrap.UploadCropbox} this
26642          */
26643         "resize" : true,
26644         /**
26645          * @event rotate
26646          * Fire when rotate the image
26647          * @param {Roo.bootstrap.UploadCropbox} this
26648          * @param {String} pos
26649          */
26650         "rotate" : true,
26651         /**
26652          * @event inspect
26653          * Fire when inspect the file
26654          * @param {Roo.bootstrap.UploadCropbox} this
26655          * @param {Object} file
26656          */
26657         "inspect" : true,
26658         /**
26659          * @event upload
26660          * Fire when xhr upload the file
26661          * @param {Roo.bootstrap.UploadCropbox} this
26662          * @param {Object} data
26663          */
26664         "upload" : true,
26665         /**
26666          * @event arrange
26667          * Fire when arrange the file data
26668          * @param {Roo.bootstrap.UploadCropbox} this
26669          * @param {Object} formData
26670          */
26671         "arrange" : true
26672     });
26673     
26674     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26675 };
26676
26677 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26678     
26679     emptyText : 'Click to upload image',
26680     rotateNotify : 'Image is too small to rotate',
26681     errorTimeout : 3000,
26682     scale : 0,
26683     baseScale : 1,
26684     rotate : 0,
26685     dragable : false,
26686     pinching : false,
26687     mouseX : 0,
26688     mouseY : 0,
26689     cropData : false,
26690     minWidth : 300,
26691     minHeight : 300,
26692     file : false,
26693     exif : {},
26694     baseRotate : 1,
26695     cropType : 'image/jpeg',
26696     buttons : false,
26697     canvasLoaded : false,
26698     isDocument : false,
26699     method : 'POST',
26700     paramName : 'imageUpload',
26701     loadMask : true,
26702     loadingText : 'Loading...',
26703     maskEl : false,
26704     
26705     getAutoCreate : function()
26706     {
26707         var cfg = {
26708             tag : 'div',
26709             cls : 'roo-upload-cropbox',
26710             cn : [
26711                 {
26712                     tag : 'input',
26713                     cls : 'roo-upload-cropbox-selector',
26714                     type : 'file'
26715                 },
26716                 {
26717                     tag : 'div',
26718                     cls : 'roo-upload-cropbox-body',
26719                     style : 'cursor:pointer',
26720                     cn : [
26721                         {
26722                             tag : 'div',
26723                             cls : 'roo-upload-cropbox-preview'
26724                         },
26725                         {
26726                             tag : 'div',
26727                             cls : 'roo-upload-cropbox-thumb'
26728                         },
26729                         {
26730                             tag : 'div',
26731                             cls : 'roo-upload-cropbox-empty-notify',
26732                             html : this.emptyText
26733                         },
26734                         {
26735                             tag : 'div',
26736                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26737                             html : this.rotateNotify
26738                         }
26739                     ]
26740                 },
26741                 {
26742                     tag : 'div',
26743                     cls : 'roo-upload-cropbox-footer',
26744                     cn : {
26745                         tag : 'div',
26746                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26747                         cn : []
26748                     }
26749                 }
26750             ]
26751         };
26752         
26753         return cfg;
26754     },
26755     
26756     onRender : function(ct, position)
26757     {
26758         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26759         
26760         if (this.buttons.length) {
26761             
26762             Roo.each(this.buttons, function(bb) {
26763                 
26764                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26765                 
26766                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26767                 
26768             }, this);
26769         }
26770         
26771         if(this.loadMask){
26772             this.maskEl = this.el;
26773         }
26774     },
26775     
26776     initEvents : function()
26777     {
26778         this.urlAPI = (window.createObjectURL && window) || 
26779                                 (window.URL && URL.revokeObjectURL && URL) || 
26780                                 (window.webkitURL && webkitURL);
26781                         
26782         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26783         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26784         
26785         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26786         this.selectorEl.hide();
26787         
26788         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26789         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26790         
26791         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26792         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26793         this.thumbEl.hide();
26794         
26795         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26796         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26797         
26798         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26799         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26800         this.errorEl.hide();
26801         
26802         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26803         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26804         this.footerEl.hide();
26805         
26806         this.setThumbBoxSize();
26807         
26808         this.bind();
26809         
26810         this.resize();
26811         
26812         this.fireEvent('initial', this);
26813     },
26814
26815     bind : function()
26816     {
26817         var _this = this;
26818         
26819         window.addEventListener("resize", function() { _this.resize(); } );
26820         
26821         this.bodyEl.on('click', this.beforeSelectFile, this);
26822         
26823         if(Roo.isTouch){
26824             this.bodyEl.on('touchstart', this.onTouchStart, this);
26825             this.bodyEl.on('touchmove', this.onTouchMove, this);
26826             this.bodyEl.on('touchend', this.onTouchEnd, this);
26827         }
26828         
26829         if(!Roo.isTouch){
26830             this.bodyEl.on('mousedown', this.onMouseDown, this);
26831             this.bodyEl.on('mousemove', this.onMouseMove, this);
26832             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26833             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26834             Roo.get(document).on('mouseup', this.onMouseUp, this);
26835         }
26836         
26837         this.selectorEl.on('change', this.onFileSelected, this);
26838     },
26839     
26840     reset : function()
26841     {    
26842         this.scale = 0;
26843         this.baseScale = 1;
26844         this.rotate = 0;
26845         this.baseRotate = 1;
26846         this.dragable = false;
26847         this.pinching = false;
26848         this.mouseX = 0;
26849         this.mouseY = 0;
26850         this.cropData = false;
26851         this.notifyEl.dom.innerHTML = this.emptyText;
26852         
26853         this.selectorEl.dom.value = '';
26854         
26855     },
26856     
26857     resize : function()
26858     {
26859         if(this.fireEvent('resize', this) != false){
26860             this.setThumbBoxPosition();
26861             this.setCanvasPosition();
26862         }
26863     },
26864     
26865     onFooterButtonClick : function(e, el, o, type)
26866     {
26867         switch (type) {
26868             case 'rotate-left' :
26869                 this.onRotateLeft(e);
26870                 break;
26871             case 'rotate-right' :
26872                 this.onRotateRight(e);
26873                 break;
26874             case 'picture' :
26875                 this.beforeSelectFile(e);
26876                 break;
26877             case 'trash' :
26878                 this.trash(e);
26879                 break;
26880             case 'crop' :
26881                 this.crop(e);
26882                 break;
26883             case 'download' :
26884                 this.download(e);
26885                 break;
26886             default :
26887                 break;
26888         }
26889         
26890         this.fireEvent('footerbuttonclick', this, type);
26891     },
26892     
26893     beforeSelectFile : function(e)
26894     {
26895         e.preventDefault();
26896         
26897         if(this.fireEvent('beforeselectfile', this) != false){
26898             this.selectorEl.dom.click();
26899         }
26900     },
26901     
26902     onFileSelected : function(e)
26903     {
26904         e.preventDefault();
26905         
26906         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26907             return;
26908         }
26909         
26910         var file = this.selectorEl.dom.files[0];
26911         
26912         if(this.fireEvent('inspect', this, file) != false){
26913             this.prepare(file);
26914         }
26915         
26916     },
26917     
26918     trash : function(e)
26919     {
26920         this.fireEvent('trash', this);
26921     },
26922     
26923     download : function(e)
26924     {
26925         this.fireEvent('download', this);
26926     },
26927     
26928     loadCanvas : function(src)
26929     {   
26930         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26931             
26932             this.reset();
26933             
26934             this.imageEl = document.createElement('img');
26935             
26936             var _this = this;
26937             
26938             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26939             
26940             this.imageEl.src = src;
26941         }
26942     },
26943     
26944     onLoadCanvas : function()
26945     {   
26946         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26947         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26948         
26949         this.bodyEl.un('click', this.beforeSelectFile, this);
26950         
26951         this.notifyEl.hide();
26952         this.thumbEl.show();
26953         this.footerEl.show();
26954         
26955         this.baseRotateLevel();
26956         
26957         if(this.isDocument){
26958             this.setThumbBoxSize();
26959         }
26960         
26961         this.setThumbBoxPosition();
26962         
26963         this.baseScaleLevel();
26964         
26965         this.draw();
26966         
26967         this.resize();
26968         
26969         this.canvasLoaded = true;
26970         
26971         if(this.loadMask){
26972             this.maskEl.unmask();
26973         }
26974         
26975     },
26976     
26977     setCanvasPosition : function()
26978     {   
26979         if(!this.canvasEl){
26980             return;
26981         }
26982         
26983         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26984         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26985         
26986         this.previewEl.setLeft(pw);
26987         this.previewEl.setTop(ph);
26988         
26989     },
26990     
26991     onMouseDown : function(e)
26992     {   
26993         e.stopEvent();
26994         
26995         this.dragable = true;
26996         this.pinching = false;
26997         
26998         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26999             this.dragable = false;
27000             return;
27001         }
27002         
27003         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27004         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27005         
27006     },
27007     
27008     onMouseMove : function(e)
27009     {   
27010         e.stopEvent();
27011         
27012         if(!this.canvasLoaded){
27013             return;
27014         }
27015         
27016         if (!this.dragable){
27017             return;
27018         }
27019         
27020         var minX = Math.ceil(this.thumbEl.getLeft(true));
27021         var minY = Math.ceil(this.thumbEl.getTop(true));
27022         
27023         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27024         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27025         
27026         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27027         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27028         
27029         x = x - this.mouseX;
27030         y = y - this.mouseY;
27031         
27032         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27033         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27034         
27035         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27036         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27037         
27038         this.previewEl.setLeft(bgX);
27039         this.previewEl.setTop(bgY);
27040         
27041         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27042         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27043     },
27044     
27045     onMouseUp : function(e)
27046     {   
27047         e.stopEvent();
27048         
27049         this.dragable = false;
27050     },
27051     
27052     onMouseWheel : function(e)
27053     {   
27054         e.stopEvent();
27055         
27056         this.startScale = this.scale;
27057         
27058         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27059         
27060         if(!this.zoomable()){
27061             this.scale = this.startScale;
27062             return;
27063         }
27064         
27065         this.draw();
27066         
27067         return;
27068     },
27069     
27070     zoomable : function()
27071     {
27072         var minScale = this.thumbEl.getWidth() / this.minWidth;
27073         
27074         if(this.minWidth < this.minHeight){
27075             minScale = this.thumbEl.getHeight() / this.minHeight;
27076         }
27077         
27078         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27079         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27080         
27081         if(
27082                 this.isDocument &&
27083                 (this.rotate == 0 || this.rotate == 180) && 
27084                 (
27085                     width > this.imageEl.OriginWidth || 
27086                     height > this.imageEl.OriginHeight ||
27087                     (width < this.minWidth && height < this.minHeight)
27088                 )
27089         ){
27090             return false;
27091         }
27092         
27093         if(
27094                 this.isDocument &&
27095                 (this.rotate == 90 || this.rotate == 270) && 
27096                 (
27097                     width > this.imageEl.OriginWidth || 
27098                     height > this.imageEl.OriginHeight ||
27099                     (width < this.minHeight && height < this.minWidth)
27100                 )
27101         ){
27102             return false;
27103         }
27104         
27105         if(
27106                 !this.isDocument &&
27107                 (this.rotate == 0 || this.rotate == 180) && 
27108                 (
27109                     width < this.minWidth || 
27110                     width > this.imageEl.OriginWidth || 
27111                     height < this.minHeight || 
27112                     height > this.imageEl.OriginHeight
27113                 )
27114         ){
27115             return false;
27116         }
27117         
27118         if(
27119                 !this.isDocument &&
27120                 (this.rotate == 90 || this.rotate == 270) && 
27121                 (
27122                     width < this.minHeight || 
27123                     width > this.imageEl.OriginWidth || 
27124                     height < this.minWidth || 
27125                     height > this.imageEl.OriginHeight
27126                 )
27127         ){
27128             return false;
27129         }
27130         
27131         return true;
27132         
27133     },
27134     
27135     onRotateLeft : function(e)
27136     {   
27137         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27138             
27139             var minScale = this.thumbEl.getWidth() / this.minWidth;
27140             
27141             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27142             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27143             
27144             this.startScale = this.scale;
27145             
27146             while (this.getScaleLevel() < minScale){
27147             
27148                 this.scale = this.scale + 1;
27149                 
27150                 if(!this.zoomable()){
27151                     break;
27152                 }
27153                 
27154                 if(
27155                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27156                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27157                 ){
27158                     continue;
27159                 }
27160                 
27161                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27162
27163                 this.draw();
27164                 
27165                 return;
27166             }
27167             
27168             this.scale = this.startScale;
27169             
27170             this.onRotateFail();
27171             
27172             return false;
27173         }
27174         
27175         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27176
27177         if(this.isDocument){
27178             this.setThumbBoxSize();
27179             this.setThumbBoxPosition();
27180             this.setCanvasPosition();
27181         }
27182         
27183         this.draw();
27184         
27185         this.fireEvent('rotate', this, 'left');
27186         
27187     },
27188     
27189     onRotateRight : function(e)
27190     {
27191         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27192             
27193             var minScale = this.thumbEl.getWidth() / this.minWidth;
27194         
27195             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27196             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27197             
27198             this.startScale = this.scale;
27199             
27200             while (this.getScaleLevel() < minScale){
27201             
27202                 this.scale = this.scale + 1;
27203                 
27204                 if(!this.zoomable()){
27205                     break;
27206                 }
27207                 
27208                 if(
27209                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27210                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27211                 ){
27212                     continue;
27213                 }
27214                 
27215                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27216
27217                 this.draw();
27218                 
27219                 return;
27220             }
27221             
27222             this.scale = this.startScale;
27223             
27224             this.onRotateFail();
27225             
27226             return false;
27227         }
27228         
27229         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27230
27231         if(this.isDocument){
27232             this.setThumbBoxSize();
27233             this.setThumbBoxPosition();
27234             this.setCanvasPosition();
27235         }
27236         
27237         this.draw();
27238         
27239         this.fireEvent('rotate', this, 'right');
27240     },
27241     
27242     onRotateFail : function()
27243     {
27244         this.errorEl.show(true);
27245         
27246         var _this = this;
27247         
27248         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27249     },
27250     
27251     draw : function()
27252     {
27253         this.previewEl.dom.innerHTML = '';
27254         
27255         var canvasEl = document.createElement("canvas");
27256         
27257         var contextEl = canvasEl.getContext("2d");
27258         
27259         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27260         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27261         var center = this.imageEl.OriginWidth / 2;
27262         
27263         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27264             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27265             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27266             center = this.imageEl.OriginHeight / 2;
27267         }
27268         
27269         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27270         
27271         contextEl.translate(center, center);
27272         contextEl.rotate(this.rotate * Math.PI / 180);
27273
27274         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27275         
27276         this.canvasEl = document.createElement("canvas");
27277         
27278         this.contextEl = this.canvasEl.getContext("2d");
27279         
27280         switch (this.rotate) {
27281             case 0 :
27282                 
27283                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27284                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27285                 
27286                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27287                 
27288                 break;
27289             case 90 : 
27290                 
27291                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27292                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27293                 
27294                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27295                     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);
27296                     break;
27297                 }
27298                 
27299                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27300                 
27301                 break;
27302             case 180 :
27303                 
27304                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27305                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27306                 
27307                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27308                     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);
27309                     break;
27310                 }
27311                 
27312                 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);
27313                 
27314                 break;
27315             case 270 :
27316                 
27317                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27318                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27319         
27320                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27321                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27322                     break;
27323                 }
27324                 
27325                 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);
27326                 
27327                 break;
27328             default : 
27329                 break;
27330         }
27331         
27332         this.previewEl.appendChild(this.canvasEl);
27333         
27334         this.setCanvasPosition();
27335     },
27336     
27337     crop : function()
27338     {
27339         if(!this.canvasLoaded){
27340             return;
27341         }
27342         
27343         var imageCanvas = document.createElement("canvas");
27344         
27345         var imageContext = imageCanvas.getContext("2d");
27346         
27347         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27348         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27349         
27350         var center = imageCanvas.width / 2;
27351         
27352         imageContext.translate(center, center);
27353         
27354         imageContext.rotate(this.rotate * Math.PI / 180);
27355         
27356         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27357         
27358         var canvas = document.createElement("canvas");
27359         
27360         var context = canvas.getContext("2d");
27361                 
27362         canvas.width = this.minWidth;
27363         canvas.height = this.minHeight;
27364
27365         switch (this.rotate) {
27366             case 0 :
27367                 
27368                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27369                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27370                 
27371                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27372                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27373                 
27374                 var targetWidth = this.minWidth - 2 * x;
27375                 var targetHeight = this.minHeight - 2 * y;
27376                 
27377                 var scale = 1;
27378                 
27379                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27380                     scale = targetWidth / width;
27381                 }
27382                 
27383                 if(x > 0 && y == 0){
27384                     scale = targetHeight / height;
27385                 }
27386                 
27387                 if(x > 0 && y > 0){
27388                     scale = targetWidth / width;
27389                     
27390                     if(width < height){
27391                         scale = targetHeight / height;
27392                     }
27393                 }
27394                 
27395                 context.scale(scale, scale);
27396                 
27397                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27398                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27399
27400                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27401                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27402
27403                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27404                 
27405                 break;
27406             case 90 : 
27407                 
27408                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27409                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27410                 
27411                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27412                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27413                 
27414                 var targetWidth = this.minWidth - 2 * x;
27415                 var targetHeight = this.minHeight - 2 * y;
27416                 
27417                 var scale = 1;
27418                 
27419                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27420                     scale = targetWidth / width;
27421                 }
27422                 
27423                 if(x > 0 && y == 0){
27424                     scale = targetHeight / height;
27425                 }
27426                 
27427                 if(x > 0 && y > 0){
27428                     scale = targetWidth / width;
27429                     
27430                     if(width < height){
27431                         scale = targetHeight / height;
27432                     }
27433                 }
27434                 
27435                 context.scale(scale, scale);
27436                 
27437                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27438                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27439
27440                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27441                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27442                 
27443                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27444                 
27445                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27446                 
27447                 break;
27448             case 180 :
27449                 
27450                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27451                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27452                 
27453                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27454                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27455                 
27456                 var targetWidth = this.minWidth - 2 * x;
27457                 var targetHeight = this.minHeight - 2 * y;
27458                 
27459                 var scale = 1;
27460                 
27461                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27462                     scale = targetWidth / width;
27463                 }
27464                 
27465                 if(x > 0 && y == 0){
27466                     scale = targetHeight / height;
27467                 }
27468                 
27469                 if(x > 0 && y > 0){
27470                     scale = targetWidth / width;
27471                     
27472                     if(width < height){
27473                         scale = targetHeight / height;
27474                     }
27475                 }
27476                 
27477                 context.scale(scale, scale);
27478                 
27479                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27480                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27481
27482                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27483                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27484
27485                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27486                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27487                 
27488                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27489                 
27490                 break;
27491             case 270 :
27492                 
27493                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27494                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27495                 
27496                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27497                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27498                 
27499                 var targetWidth = this.minWidth - 2 * x;
27500                 var targetHeight = this.minHeight - 2 * y;
27501                 
27502                 var scale = 1;
27503                 
27504                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27505                     scale = targetWidth / width;
27506                 }
27507                 
27508                 if(x > 0 && y == 0){
27509                     scale = targetHeight / height;
27510                 }
27511                 
27512                 if(x > 0 && y > 0){
27513                     scale = targetWidth / width;
27514                     
27515                     if(width < height){
27516                         scale = targetHeight / height;
27517                     }
27518                 }
27519                 
27520                 context.scale(scale, scale);
27521                 
27522                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27523                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27524
27525                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27526                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27527                 
27528                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27529                 
27530                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27531                 
27532                 break;
27533             default : 
27534                 break;
27535         }
27536         
27537         this.cropData = canvas.toDataURL(this.cropType);
27538         
27539         if(this.fireEvent('crop', this, this.cropData) !== false){
27540             this.process(this.file, this.cropData);
27541         }
27542         
27543         return;
27544         
27545     },
27546     
27547     setThumbBoxSize : function()
27548     {
27549         var width, height;
27550         
27551         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27552             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27553             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27554             
27555             this.minWidth = width;
27556             this.minHeight = height;
27557             
27558             if(this.rotate == 90 || this.rotate == 270){
27559                 this.minWidth = height;
27560                 this.minHeight = width;
27561             }
27562         }
27563         
27564         height = 300;
27565         width = Math.ceil(this.minWidth * height / this.minHeight);
27566         
27567         if(this.minWidth > this.minHeight){
27568             width = 300;
27569             height = Math.ceil(this.minHeight * width / this.minWidth);
27570         }
27571         
27572         this.thumbEl.setStyle({
27573             width : width + 'px',
27574             height : height + 'px'
27575         });
27576
27577         return;
27578             
27579     },
27580     
27581     setThumbBoxPosition : function()
27582     {
27583         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27584         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27585         
27586         this.thumbEl.setLeft(x);
27587         this.thumbEl.setTop(y);
27588         
27589     },
27590     
27591     baseRotateLevel : function()
27592     {
27593         this.baseRotate = 1;
27594         
27595         if(
27596                 typeof(this.exif) != 'undefined' &&
27597                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27598                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27599         ){
27600             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27601         }
27602         
27603         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27604         
27605     },
27606     
27607     baseScaleLevel : function()
27608     {
27609         var width, height;
27610         
27611         if(this.isDocument){
27612             
27613             if(this.baseRotate == 6 || this.baseRotate == 8){
27614             
27615                 height = this.thumbEl.getHeight();
27616                 this.baseScale = height / this.imageEl.OriginWidth;
27617
27618                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27619                     width = this.thumbEl.getWidth();
27620                     this.baseScale = width / this.imageEl.OriginHeight;
27621                 }
27622
27623                 return;
27624             }
27625
27626             height = this.thumbEl.getHeight();
27627             this.baseScale = height / this.imageEl.OriginHeight;
27628
27629             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27630                 width = this.thumbEl.getWidth();
27631                 this.baseScale = width / this.imageEl.OriginWidth;
27632             }
27633
27634             return;
27635         }
27636         
27637         if(this.baseRotate == 6 || this.baseRotate == 8){
27638             
27639             width = this.thumbEl.getHeight();
27640             this.baseScale = width / this.imageEl.OriginHeight;
27641             
27642             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27643                 height = this.thumbEl.getWidth();
27644                 this.baseScale = height / this.imageEl.OriginHeight;
27645             }
27646             
27647             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27648                 height = this.thumbEl.getWidth();
27649                 this.baseScale = height / this.imageEl.OriginHeight;
27650                 
27651                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27652                     width = this.thumbEl.getHeight();
27653                     this.baseScale = width / this.imageEl.OriginWidth;
27654                 }
27655             }
27656             
27657             return;
27658         }
27659         
27660         width = this.thumbEl.getWidth();
27661         this.baseScale = width / this.imageEl.OriginWidth;
27662         
27663         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27664             height = this.thumbEl.getHeight();
27665             this.baseScale = height / this.imageEl.OriginHeight;
27666         }
27667         
27668         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27669             
27670             height = this.thumbEl.getHeight();
27671             this.baseScale = height / this.imageEl.OriginHeight;
27672             
27673             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27674                 width = this.thumbEl.getWidth();
27675                 this.baseScale = width / this.imageEl.OriginWidth;
27676             }
27677             
27678         }
27679         
27680         return;
27681     },
27682     
27683     getScaleLevel : function()
27684     {
27685         return this.baseScale * Math.pow(1.1, this.scale);
27686     },
27687     
27688     onTouchStart : function(e)
27689     {
27690         if(!this.canvasLoaded){
27691             this.beforeSelectFile(e);
27692             return;
27693         }
27694         
27695         var touches = e.browserEvent.touches;
27696         
27697         if(!touches){
27698             return;
27699         }
27700         
27701         if(touches.length == 1){
27702             this.onMouseDown(e);
27703             return;
27704         }
27705         
27706         if(touches.length != 2){
27707             return;
27708         }
27709         
27710         var coords = [];
27711         
27712         for(var i = 0, finger; finger = touches[i]; i++){
27713             coords.push(finger.pageX, finger.pageY);
27714         }
27715         
27716         var x = Math.pow(coords[0] - coords[2], 2);
27717         var y = Math.pow(coords[1] - coords[3], 2);
27718         
27719         this.startDistance = Math.sqrt(x + y);
27720         
27721         this.startScale = this.scale;
27722         
27723         this.pinching = true;
27724         this.dragable = false;
27725         
27726     },
27727     
27728     onTouchMove : function(e)
27729     {
27730         if(!this.pinching && !this.dragable){
27731             return;
27732         }
27733         
27734         var touches = e.browserEvent.touches;
27735         
27736         if(!touches){
27737             return;
27738         }
27739         
27740         if(this.dragable){
27741             this.onMouseMove(e);
27742             return;
27743         }
27744         
27745         var coords = [];
27746         
27747         for(var i = 0, finger; finger = touches[i]; i++){
27748             coords.push(finger.pageX, finger.pageY);
27749         }
27750         
27751         var x = Math.pow(coords[0] - coords[2], 2);
27752         var y = Math.pow(coords[1] - coords[3], 2);
27753         
27754         this.endDistance = Math.sqrt(x + y);
27755         
27756         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27757         
27758         if(!this.zoomable()){
27759             this.scale = this.startScale;
27760             return;
27761         }
27762         
27763         this.draw();
27764         
27765     },
27766     
27767     onTouchEnd : function(e)
27768     {
27769         this.pinching = false;
27770         this.dragable = false;
27771         
27772     },
27773     
27774     process : function(file, crop)
27775     {
27776         if(this.loadMask){
27777             this.maskEl.mask(this.loadingText);
27778         }
27779         
27780         this.xhr = new XMLHttpRequest();
27781         
27782         file.xhr = this.xhr;
27783
27784         this.xhr.open(this.method, this.url, true);
27785         
27786         var headers = {
27787             "Accept": "application/json",
27788             "Cache-Control": "no-cache",
27789             "X-Requested-With": "XMLHttpRequest"
27790         };
27791         
27792         for (var headerName in headers) {
27793             var headerValue = headers[headerName];
27794             if (headerValue) {
27795                 this.xhr.setRequestHeader(headerName, headerValue);
27796             }
27797         }
27798         
27799         var _this = this;
27800         
27801         this.xhr.onload = function()
27802         {
27803             _this.xhrOnLoad(_this.xhr);
27804         }
27805         
27806         this.xhr.onerror = function()
27807         {
27808             _this.xhrOnError(_this.xhr);
27809         }
27810         
27811         var formData = new FormData();
27812
27813         formData.append('returnHTML', 'NO');
27814         
27815         if(crop){
27816             formData.append('crop', crop);
27817         }
27818         
27819         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27820             formData.append(this.paramName, file, file.name);
27821         }
27822         
27823         if(typeof(file.filename) != 'undefined'){
27824             formData.append('filename', file.filename);
27825         }
27826         
27827         if(typeof(file.mimetype) != 'undefined'){
27828             formData.append('mimetype', file.mimetype);
27829         }
27830         
27831         if(this.fireEvent('arrange', this, formData) != false){
27832             this.xhr.send(formData);
27833         };
27834     },
27835     
27836     xhrOnLoad : function(xhr)
27837     {
27838         if(this.loadMask){
27839             this.maskEl.unmask();
27840         }
27841         
27842         if (xhr.readyState !== 4) {
27843             this.fireEvent('exception', this, xhr);
27844             return;
27845         }
27846
27847         var response = Roo.decode(xhr.responseText);
27848         
27849         if(!response.success){
27850             this.fireEvent('exception', this, xhr);
27851             return;
27852         }
27853         
27854         var response = Roo.decode(xhr.responseText);
27855         
27856         this.fireEvent('upload', this, response);
27857         
27858     },
27859     
27860     xhrOnError : function()
27861     {
27862         if(this.loadMask){
27863             this.maskEl.unmask();
27864         }
27865         
27866         Roo.log('xhr on error');
27867         
27868         var response = Roo.decode(xhr.responseText);
27869           
27870         Roo.log(response);
27871         
27872     },
27873     
27874     prepare : function(file)
27875     {   
27876         if(this.loadMask){
27877             this.maskEl.mask(this.loadingText);
27878         }
27879         
27880         this.file = false;
27881         this.exif = {};
27882         
27883         if(typeof(file) === 'string'){
27884             this.loadCanvas(file);
27885             return;
27886         }
27887         
27888         if(!file || !this.urlAPI){
27889             return;
27890         }
27891         
27892         this.file = file;
27893         this.cropType = file.type;
27894         
27895         var _this = this;
27896         
27897         if(this.fireEvent('prepare', this, this.file) != false){
27898             
27899             var reader = new FileReader();
27900             
27901             reader.onload = function (e) {
27902                 if (e.target.error) {
27903                     Roo.log(e.target.error);
27904                     return;
27905                 }
27906                 
27907                 var buffer = e.target.result,
27908                     dataView = new DataView(buffer),
27909                     offset = 2,
27910                     maxOffset = dataView.byteLength - 4,
27911                     markerBytes,
27912                     markerLength;
27913                 
27914                 if (dataView.getUint16(0) === 0xffd8) {
27915                     while (offset < maxOffset) {
27916                         markerBytes = dataView.getUint16(offset);
27917                         
27918                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27919                             markerLength = dataView.getUint16(offset + 2) + 2;
27920                             if (offset + markerLength > dataView.byteLength) {
27921                                 Roo.log('Invalid meta data: Invalid segment size.');
27922                                 break;
27923                             }
27924                             
27925                             if(markerBytes == 0xffe1){
27926                                 _this.parseExifData(
27927                                     dataView,
27928                                     offset,
27929                                     markerLength
27930                                 );
27931                             }
27932                             
27933                             offset += markerLength;
27934                             
27935                             continue;
27936                         }
27937                         
27938                         break;
27939                     }
27940                     
27941                 }
27942                 
27943                 var url = _this.urlAPI.createObjectURL(_this.file);
27944                 
27945                 _this.loadCanvas(url);
27946                 
27947                 return;
27948             }
27949             
27950             reader.readAsArrayBuffer(this.file);
27951             
27952         }
27953         
27954     },
27955     
27956     parseExifData : function(dataView, offset, length)
27957     {
27958         var tiffOffset = offset + 10,
27959             littleEndian,
27960             dirOffset;
27961     
27962         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27963             // No Exif data, might be XMP data instead
27964             return;
27965         }
27966         
27967         // Check for the ASCII code for "Exif" (0x45786966):
27968         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27969             // No Exif data, might be XMP data instead
27970             return;
27971         }
27972         if (tiffOffset + 8 > dataView.byteLength) {
27973             Roo.log('Invalid Exif data: Invalid segment size.');
27974             return;
27975         }
27976         // Check for the two null bytes:
27977         if (dataView.getUint16(offset + 8) !== 0x0000) {
27978             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27979             return;
27980         }
27981         // Check the byte alignment:
27982         switch (dataView.getUint16(tiffOffset)) {
27983         case 0x4949:
27984             littleEndian = true;
27985             break;
27986         case 0x4D4D:
27987             littleEndian = false;
27988             break;
27989         default:
27990             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27991             return;
27992         }
27993         // Check for the TIFF tag marker (0x002A):
27994         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27995             Roo.log('Invalid Exif data: Missing TIFF marker.');
27996             return;
27997         }
27998         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27999         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28000         
28001         this.parseExifTags(
28002             dataView,
28003             tiffOffset,
28004             tiffOffset + dirOffset,
28005             littleEndian
28006         );
28007     },
28008     
28009     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28010     {
28011         var tagsNumber,
28012             dirEndOffset,
28013             i;
28014         if (dirOffset + 6 > dataView.byteLength) {
28015             Roo.log('Invalid Exif data: Invalid directory offset.');
28016             return;
28017         }
28018         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28019         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28020         if (dirEndOffset + 4 > dataView.byteLength) {
28021             Roo.log('Invalid Exif data: Invalid directory size.');
28022             return;
28023         }
28024         for (i = 0; i < tagsNumber; i += 1) {
28025             this.parseExifTag(
28026                 dataView,
28027                 tiffOffset,
28028                 dirOffset + 2 + 12 * i, // tag offset
28029                 littleEndian
28030             );
28031         }
28032         // Return the offset to the next directory:
28033         return dataView.getUint32(dirEndOffset, littleEndian);
28034     },
28035     
28036     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28037     {
28038         var tag = dataView.getUint16(offset, littleEndian);
28039         
28040         this.exif[tag] = this.getExifValue(
28041             dataView,
28042             tiffOffset,
28043             offset,
28044             dataView.getUint16(offset + 2, littleEndian), // tag type
28045             dataView.getUint32(offset + 4, littleEndian), // tag length
28046             littleEndian
28047         );
28048     },
28049     
28050     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28051     {
28052         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28053             tagSize,
28054             dataOffset,
28055             values,
28056             i,
28057             str,
28058             c;
28059     
28060         if (!tagType) {
28061             Roo.log('Invalid Exif data: Invalid tag type.');
28062             return;
28063         }
28064         
28065         tagSize = tagType.size * length;
28066         // Determine if the value is contained in the dataOffset bytes,
28067         // or if the value at the dataOffset is a pointer to the actual data:
28068         dataOffset = tagSize > 4 ?
28069                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28070         if (dataOffset + tagSize > dataView.byteLength) {
28071             Roo.log('Invalid Exif data: Invalid data offset.');
28072             return;
28073         }
28074         if (length === 1) {
28075             return tagType.getValue(dataView, dataOffset, littleEndian);
28076         }
28077         values = [];
28078         for (i = 0; i < length; i += 1) {
28079             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28080         }
28081         
28082         if (tagType.ascii) {
28083             str = '';
28084             // Concatenate the chars:
28085             for (i = 0; i < values.length; i += 1) {
28086                 c = values[i];
28087                 // Ignore the terminating NULL byte(s):
28088                 if (c === '\u0000') {
28089                     break;
28090                 }
28091                 str += c;
28092             }
28093             return str;
28094         }
28095         return values;
28096     }
28097     
28098 });
28099
28100 Roo.apply(Roo.bootstrap.UploadCropbox, {
28101     tags : {
28102         'Orientation': 0x0112
28103     },
28104     
28105     Orientation: {
28106             1: 0, //'top-left',
28107 //            2: 'top-right',
28108             3: 180, //'bottom-right',
28109 //            4: 'bottom-left',
28110 //            5: 'left-top',
28111             6: 90, //'right-top',
28112 //            7: 'right-bottom',
28113             8: 270 //'left-bottom'
28114     },
28115     
28116     exifTagTypes : {
28117         // byte, 8-bit unsigned int:
28118         1: {
28119             getValue: function (dataView, dataOffset) {
28120                 return dataView.getUint8(dataOffset);
28121             },
28122             size: 1
28123         },
28124         // ascii, 8-bit byte:
28125         2: {
28126             getValue: function (dataView, dataOffset) {
28127                 return String.fromCharCode(dataView.getUint8(dataOffset));
28128             },
28129             size: 1,
28130             ascii: true
28131         },
28132         // short, 16 bit int:
28133         3: {
28134             getValue: function (dataView, dataOffset, littleEndian) {
28135                 return dataView.getUint16(dataOffset, littleEndian);
28136             },
28137             size: 2
28138         },
28139         // long, 32 bit int:
28140         4: {
28141             getValue: function (dataView, dataOffset, littleEndian) {
28142                 return dataView.getUint32(dataOffset, littleEndian);
28143             },
28144             size: 4
28145         },
28146         // rational = two long values, first is numerator, second is denominator:
28147         5: {
28148             getValue: function (dataView, dataOffset, littleEndian) {
28149                 return dataView.getUint32(dataOffset, littleEndian) /
28150                     dataView.getUint32(dataOffset + 4, littleEndian);
28151             },
28152             size: 8
28153         },
28154         // slong, 32 bit signed int:
28155         9: {
28156             getValue: function (dataView, dataOffset, littleEndian) {
28157                 return dataView.getInt32(dataOffset, littleEndian);
28158             },
28159             size: 4
28160         },
28161         // srational, two slongs, first is numerator, second is denominator:
28162         10: {
28163             getValue: function (dataView, dataOffset, littleEndian) {
28164                 return dataView.getInt32(dataOffset, littleEndian) /
28165                     dataView.getInt32(dataOffset + 4, littleEndian);
28166             },
28167             size: 8
28168         }
28169     },
28170     
28171     footer : {
28172         STANDARD : [
28173             {
28174                 tag : 'div',
28175                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28176                 action : 'rotate-left',
28177                 cn : [
28178                     {
28179                         tag : 'button',
28180                         cls : 'btn btn-default',
28181                         html : '<i class="fa fa-undo"></i>'
28182                     }
28183                 ]
28184             },
28185             {
28186                 tag : 'div',
28187                 cls : 'btn-group roo-upload-cropbox-picture',
28188                 action : 'picture',
28189                 cn : [
28190                     {
28191                         tag : 'button',
28192                         cls : 'btn btn-default',
28193                         html : '<i class="fa fa-picture-o"></i>'
28194                     }
28195                 ]
28196             },
28197             {
28198                 tag : 'div',
28199                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28200                 action : 'rotate-right',
28201                 cn : [
28202                     {
28203                         tag : 'button',
28204                         cls : 'btn btn-default',
28205                         html : '<i class="fa fa-repeat"></i>'
28206                     }
28207                 ]
28208             }
28209         ],
28210         DOCUMENT : [
28211             {
28212                 tag : 'div',
28213                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28214                 action : 'rotate-left',
28215                 cn : [
28216                     {
28217                         tag : 'button',
28218                         cls : 'btn btn-default',
28219                         html : '<i class="fa fa-undo"></i>'
28220                     }
28221                 ]
28222             },
28223             {
28224                 tag : 'div',
28225                 cls : 'btn-group roo-upload-cropbox-download',
28226                 action : 'download',
28227                 cn : [
28228                     {
28229                         tag : 'button',
28230                         cls : 'btn btn-default',
28231                         html : '<i class="fa fa-download"></i>'
28232                     }
28233                 ]
28234             },
28235             {
28236                 tag : 'div',
28237                 cls : 'btn-group roo-upload-cropbox-crop',
28238                 action : 'crop',
28239                 cn : [
28240                     {
28241                         tag : 'button',
28242                         cls : 'btn btn-default',
28243                         html : '<i class="fa fa-crop"></i>'
28244                     }
28245                 ]
28246             },
28247             {
28248                 tag : 'div',
28249                 cls : 'btn-group roo-upload-cropbox-trash',
28250                 action : 'trash',
28251                 cn : [
28252                     {
28253                         tag : 'button',
28254                         cls : 'btn btn-default',
28255                         html : '<i class="fa fa-trash"></i>'
28256                     }
28257                 ]
28258             },
28259             {
28260                 tag : 'div',
28261                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28262                 action : 'rotate-right',
28263                 cn : [
28264                     {
28265                         tag : 'button',
28266                         cls : 'btn btn-default',
28267                         html : '<i class="fa fa-repeat"></i>'
28268                     }
28269                 ]
28270             }
28271         ],
28272         ROTATOR : [
28273             {
28274                 tag : 'div',
28275                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28276                 action : 'rotate-left',
28277                 cn : [
28278                     {
28279                         tag : 'button',
28280                         cls : 'btn btn-default',
28281                         html : '<i class="fa fa-undo"></i>'
28282                     }
28283                 ]
28284             },
28285             {
28286                 tag : 'div',
28287                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28288                 action : 'rotate-right',
28289                 cn : [
28290                     {
28291                         tag : 'button',
28292                         cls : 'btn btn-default',
28293                         html : '<i class="fa fa-repeat"></i>'
28294                     }
28295                 ]
28296             }
28297         ]
28298     }
28299 });
28300
28301 /*
28302 * Licence: LGPL
28303 */
28304
28305 /**
28306  * @class Roo.bootstrap.DocumentManager
28307  * @extends Roo.bootstrap.Component
28308  * Bootstrap DocumentManager class
28309  * @cfg {String} paramName default 'imageUpload'
28310  * @cfg {String} toolTipName default 'filename'
28311  * @cfg {String} method default POST
28312  * @cfg {String} url action url
28313  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28314  * @cfg {Boolean} multiple multiple upload default true
28315  * @cfg {Number} thumbSize default 300
28316  * @cfg {String} fieldLabel
28317  * @cfg {Number} labelWidth default 4
28318  * @cfg {String} labelAlign (left|top) default left
28319  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28320 * @cfg {Number} labellg set the width of label (1-12)
28321  * @cfg {Number} labelmd set the width of label (1-12)
28322  * @cfg {Number} labelsm set the width of label (1-12)
28323  * @cfg {Number} labelxs set the width of label (1-12)
28324  * 
28325  * @constructor
28326  * Create a new DocumentManager
28327  * @param {Object} config The config object
28328  */
28329
28330 Roo.bootstrap.DocumentManager = function(config){
28331     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28332     
28333     this.files = [];
28334     this.delegates = [];
28335     
28336     this.addEvents({
28337         /**
28338          * @event initial
28339          * Fire when initial the DocumentManager
28340          * @param {Roo.bootstrap.DocumentManager} this
28341          */
28342         "initial" : true,
28343         /**
28344          * @event inspect
28345          * inspect selected file
28346          * @param {Roo.bootstrap.DocumentManager} this
28347          * @param {File} file
28348          */
28349         "inspect" : true,
28350         /**
28351          * @event exception
28352          * Fire when xhr load exception
28353          * @param {Roo.bootstrap.DocumentManager} this
28354          * @param {XMLHttpRequest} xhr
28355          */
28356         "exception" : true,
28357         /**
28358          * @event afterupload
28359          * Fire when xhr load exception
28360          * @param {Roo.bootstrap.DocumentManager} this
28361          * @param {XMLHttpRequest} xhr
28362          */
28363         "afterupload" : true,
28364         /**
28365          * @event prepare
28366          * prepare the form data
28367          * @param {Roo.bootstrap.DocumentManager} this
28368          * @param {Object} formData
28369          */
28370         "prepare" : true,
28371         /**
28372          * @event remove
28373          * Fire when remove the file
28374          * @param {Roo.bootstrap.DocumentManager} this
28375          * @param {Object} file
28376          */
28377         "remove" : true,
28378         /**
28379          * @event refresh
28380          * Fire after refresh the file
28381          * @param {Roo.bootstrap.DocumentManager} this
28382          */
28383         "refresh" : true,
28384         /**
28385          * @event click
28386          * Fire after click the image
28387          * @param {Roo.bootstrap.DocumentManager} this
28388          * @param {Object} file
28389          */
28390         "click" : true,
28391         /**
28392          * @event edit
28393          * Fire when upload a image and editable set to true
28394          * @param {Roo.bootstrap.DocumentManager} this
28395          * @param {Object} file
28396          */
28397         "edit" : true,
28398         /**
28399          * @event beforeselectfile
28400          * Fire before select file
28401          * @param {Roo.bootstrap.DocumentManager} this
28402          */
28403         "beforeselectfile" : true,
28404         /**
28405          * @event process
28406          * Fire before process file
28407          * @param {Roo.bootstrap.DocumentManager} this
28408          * @param {Object} file
28409          */
28410         "process" : true
28411         
28412     });
28413 };
28414
28415 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28416     
28417     boxes : 0,
28418     inputName : '',
28419     thumbSize : 300,
28420     multiple : true,
28421     files : false,
28422     method : 'POST',
28423     url : '',
28424     paramName : 'imageUpload',
28425     toolTipName : 'filename',
28426     fieldLabel : '',
28427     labelWidth : 4,
28428     labelAlign : 'left',
28429     editable : true,
28430     delegates : false,
28431     xhr : false, 
28432     
28433     labellg : 0,
28434     labelmd : 0,
28435     labelsm : 0,
28436     labelxs : 0,
28437     
28438     getAutoCreate : function()
28439     {   
28440         var managerWidget = {
28441             tag : 'div',
28442             cls : 'roo-document-manager',
28443             cn : [
28444                 {
28445                     tag : 'input',
28446                     cls : 'roo-document-manager-selector',
28447                     type : 'file'
28448                 },
28449                 {
28450                     tag : 'div',
28451                     cls : 'roo-document-manager-uploader',
28452                     cn : [
28453                         {
28454                             tag : 'div',
28455                             cls : 'roo-document-manager-upload-btn',
28456                             html : '<i class="fa fa-plus"></i>'
28457                         }
28458                     ]
28459                     
28460                 }
28461             ]
28462         };
28463         
28464         var content = [
28465             {
28466                 tag : 'div',
28467                 cls : 'column col-md-12',
28468                 cn : managerWidget
28469             }
28470         ];
28471         
28472         if(this.fieldLabel.length){
28473             
28474             content = [
28475                 {
28476                     tag : 'div',
28477                     cls : 'column col-md-12',
28478                     html : this.fieldLabel
28479                 },
28480                 {
28481                     tag : 'div',
28482                     cls : 'column col-md-12',
28483                     cn : managerWidget
28484                 }
28485             ];
28486
28487             if(this.labelAlign == 'left'){
28488                 content = [
28489                     {
28490                         tag : 'div',
28491                         cls : 'column',
28492                         html : this.fieldLabel
28493                     },
28494                     {
28495                         tag : 'div',
28496                         cls : 'column',
28497                         cn : managerWidget
28498                     }
28499                 ];
28500                 
28501                 if(this.labelWidth > 12){
28502                     content[0].style = "width: " + this.labelWidth + 'px';
28503                 }
28504
28505                 if(this.labelWidth < 13 && this.labelmd == 0){
28506                     this.labelmd = this.labelWidth;
28507                 }
28508
28509                 if(this.labellg > 0){
28510                     content[0].cls += ' col-lg-' + this.labellg;
28511                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28512                 }
28513
28514                 if(this.labelmd > 0){
28515                     content[0].cls += ' col-md-' + this.labelmd;
28516                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28517                 }
28518
28519                 if(this.labelsm > 0){
28520                     content[0].cls += ' col-sm-' + this.labelsm;
28521                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28522                 }
28523
28524                 if(this.labelxs > 0){
28525                     content[0].cls += ' col-xs-' + this.labelxs;
28526                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28527                 }
28528                 
28529             }
28530         }
28531         
28532         var cfg = {
28533             tag : 'div',
28534             cls : 'row clearfix',
28535             cn : content
28536         };
28537         
28538         return cfg;
28539         
28540     },
28541     
28542     initEvents : function()
28543     {
28544         this.managerEl = this.el.select('.roo-document-manager', true).first();
28545         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28546         
28547         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28548         this.selectorEl.hide();
28549         
28550         if(this.multiple){
28551             this.selectorEl.attr('multiple', 'multiple');
28552         }
28553         
28554         this.selectorEl.on('change', this.onFileSelected, this);
28555         
28556         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28557         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28558         
28559         this.uploader.on('click', this.onUploaderClick, this);
28560         
28561         this.renderProgressDialog();
28562         
28563         var _this = this;
28564         
28565         window.addEventListener("resize", function() { _this.refresh(); } );
28566         
28567         this.fireEvent('initial', this);
28568     },
28569     
28570     renderProgressDialog : function()
28571     {
28572         var _this = this;
28573         
28574         this.progressDialog = new Roo.bootstrap.Modal({
28575             cls : 'roo-document-manager-progress-dialog',
28576             allow_close : false,
28577             title : '',
28578             buttons : [
28579                 {
28580                     name  :'cancel',
28581                     weight : 'danger',
28582                     html : 'Cancel'
28583                 }
28584             ], 
28585             listeners : { 
28586                 btnclick : function() {
28587                     _this.uploadCancel();
28588                     this.hide();
28589                 }
28590             }
28591         });
28592          
28593         this.progressDialog.render(Roo.get(document.body));
28594          
28595         this.progress = new Roo.bootstrap.Progress({
28596             cls : 'roo-document-manager-progress',
28597             active : true,
28598             striped : true
28599         });
28600         
28601         this.progress.render(this.progressDialog.getChildContainer());
28602         
28603         this.progressBar = new Roo.bootstrap.ProgressBar({
28604             cls : 'roo-document-manager-progress-bar',
28605             aria_valuenow : 0,
28606             aria_valuemin : 0,
28607             aria_valuemax : 12,
28608             panel : 'success'
28609         });
28610         
28611         this.progressBar.render(this.progress.getChildContainer());
28612     },
28613     
28614     onUploaderClick : function(e)
28615     {
28616         e.preventDefault();
28617      
28618         if(this.fireEvent('beforeselectfile', this) != false){
28619             this.selectorEl.dom.click();
28620         }
28621         
28622     },
28623     
28624     onFileSelected : function(e)
28625     {
28626         e.preventDefault();
28627         
28628         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28629             return;
28630         }
28631         
28632         Roo.each(this.selectorEl.dom.files, function(file){
28633             if(this.fireEvent('inspect', this, file) != false){
28634                 this.files.push(file);
28635             }
28636         }, this);
28637         
28638         this.queue();
28639         
28640     },
28641     
28642     queue : function()
28643     {
28644         this.selectorEl.dom.value = '';
28645         
28646         if(!this.files.length){
28647             return;
28648         }
28649         
28650         if(this.boxes > 0 && this.files.length > this.boxes){
28651             this.files = this.files.slice(0, this.boxes);
28652         }
28653         
28654         this.uploader.show();
28655         
28656         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28657             this.uploader.hide();
28658         }
28659         
28660         var _this = this;
28661         
28662         var files = [];
28663         
28664         var docs = [];
28665         
28666         Roo.each(this.files, function(file){
28667             
28668             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28669                 var f = this.renderPreview(file);
28670                 files.push(f);
28671                 return;
28672             }
28673             
28674             if(file.type.indexOf('image') != -1){
28675                 this.delegates.push(
28676                     (function(){
28677                         _this.process(file);
28678                     }).createDelegate(this)
28679                 );
28680         
28681                 return;
28682             }
28683             
28684             docs.push(
28685                 (function(){
28686                     _this.process(file);
28687                 }).createDelegate(this)
28688             );
28689             
28690         }, this);
28691         
28692         this.files = files;
28693         
28694         this.delegates = this.delegates.concat(docs);
28695         
28696         if(!this.delegates.length){
28697             this.refresh();
28698             return;
28699         }
28700         
28701         this.progressBar.aria_valuemax = this.delegates.length;
28702         
28703         this.arrange();
28704         
28705         return;
28706     },
28707     
28708     arrange : function()
28709     {
28710         if(!this.delegates.length){
28711             this.progressDialog.hide();
28712             this.refresh();
28713             return;
28714         }
28715         
28716         var delegate = this.delegates.shift();
28717         
28718         this.progressDialog.show();
28719         
28720         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28721         
28722         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28723         
28724         delegate();
28725     },
28726     
28727     refresh : function()
28728     {
28729         this.uploader.show();
28730         
28731         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28732             this.uploader.hide();
28733         }
28734         
28735         Roo.isTouch ? this.closable(false) : this.closable(true);
28736         
28737         this.fireEvent('refresh', this);
28738     },
28739     
28740     onRemove : function(e, el, o)
28741     {
28742         e.preventDefault();
28743         
28744         this.fireEvent('remove', this, o);
28745         
28746     },
28747     
28748     remove : function(o)
28749     {
28750         var files = [];
28751         
28752         Roo.each(this.files, function(file){
28753             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28754                 files.push(file);
28755                 return;
28756             }
28757
28758             o.target.remove();
28759
28760         }, this);
28761         
28762         this.files = files;
28763         
28764         this.refresh();
28765     },
28766     
28767     clear : function()
28768     {
28769         Roo.each(this.files, function(file){
28770             if(!file.target){
28771                 return;
28772             }
28773             
28774             file.target.remove();
28775
28776         }, this);
28777         
28778         this.files = [];
28779         
28780         this.refresh();
28781     },
28782     
28783     onClick : function(e, el, o)
28784     {
28785         e.preventDefault();
28786         
28787         this.fireEvent('click', this, o);
28788         
28789     },
28790     
28791     closable : function(closable)
28792     {
28793         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28794             
28795             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28796             
28797             if(closable){
28798                 el.show();
28799                 return;
28800             }
28801             
28802             el.hide();
28803             
28804         }, this);
28805     },
28806     
28807     xhrOnLoad : function(xhr)
28808     {
28809         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28810             el.remove();
28811         }, this);
28812         
28813         if (xhr.readyState !== 4) {
28814             this.arrange();
28815             this.fireEvent('exception', this, xhr);
28816             return;
28817         }
28818
28819         var response = Roo.decode(xhr.responseText);
28820         
28821         if(!response.success){
28822             this.arrange();
28823             this.fireEvent('exception', this, xhr);
28824             return;
28825         }
28826         
28827         var file = this.renderPreview(response.data);
28828         
28829         this.files.push(file);
28830         
28831         this.arrange();
28832         
28833         this.fireEvent('afterupload', this, xhr);
28834         
28835     },
28836     
28837     xhrOnError : function(xhr)
28838     {
28839         Roo.log('xhr on error');
28840         
28841         var response = Roo.decode(xhr.responseText);
28842           
28843         Roo.log(response);
28844         
28845         this.arrange();
28846     },
28847     
28848     process : function(file)
28849     {
28850         if(this.fireEvent('process', this, file) !== false){
28851             if(this.editable && file.type.indexOf('image') != -1){
28852                 this.fireEvent('edit', this, file);
28853                 return;
28854             }
28855
28856             this.uploadStart(file, false);
28857
28858             return;
28859         }
28860         
28861     },
28862     
28863     uploadStart : function(file, crop)
28864     {
28865         this.xhr = new XMLHttpRequest();
28866         
28867         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28868             this.arrange();
28869             return;
28870         }
28871         
28872         file.xhr = this.xhr;
28873             
28874         this.managerEl.createChild({
28875             tag : 'div',
28876             cls : 'roo-document-manager-loading',
28877             cn : [
28878                 {
28879                     tag : 'div',
28880                     tooltip : file.name,
28881                     cls : 'roo-document-manager-thumb',
28882                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28883                 }
28884             ]
28885
28886         });
28887
28888         this.xhr.open(this.method, this.url, true);
28889         
28890         var headers = {
28891             "Accept": "application/json",
28892             "Cache-Control": "no-cache",
28893             "X-Requested-With": "XMLHttpRequest"
28894         };
28895         
28896         for (var headerName in headers) {
28897             var headerValue = headers[headerName];
28898             if (headerValue) {
28899                 this.xhr.setRequestHeader(headerName, headerValue);
28900             }
28901         }
28902         
28903         var _this = this;
28904         
28905         this.xhr.onload = function()
28906         {
28907             _this.xhrOnLoad(_this.xhr);
28908         }
28909         
28910         this.xhr.onerror = function()
28911         {
28912             _this.xhrOnError(_this.xhr);
28913         }
28914         
28915         var formData = new FormData();
28916
28917         formData.append('returnHTML', 'NO');
28918         
28919         if(crop){
28920             formData.append('crop', crop);
28921         }
28922         
28923         formData.append(this.paramName, file, file.name);
28924         
28925         var options = {
28926             file : file, 
28927             manually : false
28928         };
28929         
28930         if(this.fireEvent('prepare', this, formData, options) != false){
28931             
28932             if(options.manually){
28933                 return;
28934             }
28935             
28936             this.xhr.send(formData);
28937             return;
28938         };
28939         
28940         this.uploadCancel();
28941     },
28942     
28943     uploadCancel : function()
28944     {
28945         if (this.xhr) {
28946             this.xhr.abort();
28947         }
28948         
28949         this.delegates = [];
28950         
28951         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28952             el.remove();
28953         }, this);
28954         
28955         this.arrange();
28956     },
28957     
28958     renderPreview : function(file)
28959     {
28960         if(typeof(file.target) != 'undefined' && file.target){
28961             return file;
28962         }
28963         
28964         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28965         
28966         var previewEl = this.managerEl.createChild({
28967             tag : 'div',
28968             cls : 'roo-document-manager-preview',
28969             cn : [
28970                 {
28971                     tag : 'div',
28972                     tooltip : file[this.toolTipName],
28973                     cls : 'roo-document-manager-thumb',
28974                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28975                 },
28976                 {
28977                     tag : 'button',
28978                     cls : 'close',
28979                     html : '<i class="fa fa-times-circle"></i>'
28980                 }
28981             ]
28982         });
28983
28984         var close = previewEl.select('button.close', true).first();
28985
28986         close.on('click', this.onRemove, this, file);
28987
28988         file.target = previewEl;
28989
28990         var image = previewEl.select('img', true).first();
28991         
28992         var _this = this;
28993         
28994         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28995         
28996         image.on('click', this.onClick, this, file);
28997         
28998         return file;
28999         
29000     },
29001     
29002     onPreviewLoad : function(file, image)
29003     {
29004         if(typeof(file.target) == 'undefined' || !file.target){
29005             return;
29006         }
29007         
29008         var width = image.dom.naturalWidth || image.dom.width;
29009         var height = image.dom.naturalHeight || image.dom.height;
29010         
29011         if(width > height){
29012             file.target.addClass('wide');
29013             return;
29014         }
29015         
29016         file.target.addClass('tall');
29017         return;
29018         
29019     },
29020     
29021     uploadFromSource : function(file, crop)
29022     {
29023         this.xhr = new XMLHttpRequest();
29024         
29025         this.managerEl.createChild({
29026             tag : 'div',
29027             cls : 'roo-document-manager-loading',
29028             cn : [
29029                 {
29030                     tag : 'div',
29031                     tooltip : file.name,
29032                     cls : 'roo-document-manager-thumb',
29033                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29034                 }
29035             ]
29036
29037         });
29038
29039         this.xhr.open(this.method, this.url, true);
29040         
29041         var headers = {
29042             "Accept": "application/json",
29043             "Cache-Control": "no-cache",
29044             "X-Requested-With": "XMLHttpRequest"
29045         };
29046         
29047         for (var headerName in headers) {
29048             var headerValue = headers[headerName];
29049             if (headerValue) {
29050                 this.xhr.setRequestHeader(headerName, headerValue);
29051             }
29052         }
29053         
29054         var _this = this;
29055         
29056         this.xhr.onload = function()
29057         {
29058             _this.xhrOnLoad(_this.xhr);
29059         }
29060         
29061         this.xhr.onerror = function()
29062         {
29063             _this.xhrOnError(_this.xhr);
29064         }
29065         
29066         var formData = new FormData();
29067
29068         formData.append('returnHTML', 'NO');
29069         
29070         formData.append('crop', crop);
29071         
29072         if(typeof(file.filename) != 'undefined'){
29073             formData.append('filename', file.filename);
29074         }
29075         
29076         if(typeof(file.mimetype) != 'undefined'){
29077             formData.append('mimetype', file.mimetype);
29078         }
29079         
29080         Roo.log(formData);
29081         
29082         if(this.fireEvent('prepare', this, formData) != false){
29083             this.xhr.send(formData);
29084         };
29085     }
29086 });
29087
29088 /*
29089 * Licence: LGPL
29090 */
29091
29092 /**
29093  * @class Roo.bootstrap.DocumentViewer
29094  * @extends Roo.bootstrap.Component
29095  * Bootstrap DocumentViewer class
29096  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29097  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29098  * 
29099  * @constructor
29100  * Create a new DocumentViewer
29101  * @param {Object} config The config object
29102  */
29103
29104 Roo.bootstrap.DocumentViewer = function(config){
29105     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29106     
29107     this.addEvents({
29108         /**
29109          * @event initial
29110          * Fire after initEvent
29111          * @param {Roo.bootstrap.DocumentViewer} this
29112          */
29113         "initial" : true,
29114         /**
29115          * @event click
29116          * Fire after click
29117          * @param {Roo.bootstrap.DocumentViewer} this
29118          */
29119         "click" : true,
29120         /**
29121          * @event download
29122          * Fire after download button
29123          * @param {Roo.bootstrap.DocumentViewer} this
29124          */
29125         "download" : true,
29126         /**
29127          * @event trash
29128          * Fire after trash button
29129          * @param {Roo.bootstrap.DocumentViewer} this
29130          */
29131         "trash" : true
29132         
29133     });
29134 };
29135
29136 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29137     
29138     showDownload : true,
29139     
29140     showTrash : true,
29141     
29142     getAutoCreate : function()
29143     {
29144         var cfg = {
29145             tag : 'div',
29146             cls : 'roo-document-viewer',
29147             cn : [
29148                 {
29149                     tag : 'div',
29150                     cls : 'roo-document-viewer-body',
29151                     cn : [
29152                         {
29153                             tag : 'div',
29154                             cls : 'roo-document-viewer-thumb',
29155                             cn : [
29156                                 {
29157                                     tag : 'img',
29158                                     cls : 'roo-document-viewer-image'
29159                                 }
29160                             ]
29161                         }
29162                     ]
29163                 },
29164                 {
29165                     tag : 'div',
29166                     cls : 'roo-document-viewer-footer',
29167                     cn : {
29168                         tag : 'div',
29169                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29170                         cn : [
29171                             {
29172                                 tag : 'div',
29173                                 cls : 'btn-group roo-document-viewer-download',
29174                                 cn : [
29175                                     {
29176                                         tag : 'button',
29177                                         cls : 'btn btn-default',
29178                                         html : '<i class="fa fa-download"></i>'
29179                                     }
29180                                 ]
29181                             },
29182                             {
29183                                 tag : 'div',
29184                                 cls : 'btn-group roo-document-viewer-trash',
29185                                 cn : [
29186                                     {
29187                                         tag : 'button',
29188                                         cls : 'btn btn-default',
29189                                         html : '<i class="fa fa-trash"></i>'
29190                                     }
29191                                 ]
29192                             }
29193                         ]
29194                     }
29195                 }
29196             ]
29197         };
29198         
29199         return cfg;
29200     },
29201     
29202     initEvents : function()
29203     {
29204         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29205         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29206         
29207         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29208         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29209         
29210         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29211         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29212         
29213         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29214         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29215         
29216         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29217         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29218         
29219         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29220         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29221         
29222         this.bodyEl.on('click', this.onClick, this);
29223         this.downloadBtn.on('click', this.onDownload, this);
29224         this.trashBtn.on('click', this.onTrash, this);
29225         
29226         this.downloadBtn.hide();
29227         this.trashBtn.hide();
29228         
29229         if(this.showDownload){
29230             this.downloadBtn.show();
29231         }
29232         
29233         if(this.showTrash){
29234             this.trashBtn.show();
29235         }
29236         
29237         if(!this.showDownload && !this.showTrash) {
29238             this.footerEl.hide();
29239         }
29240         
29241     },
29242     
29243     initial : function()
29244     {
29245         this.fireEvent('initial', this);
29246         
29247     },
29248     
29249     onClick : function(e)
29250     {
29251         e.preventDefault();
29252         
29253         this.fireEvent('click', this);
29254     },
29255     
29256     onDownload : function(e)
29257     {
29258         e.preventDefault();
29259         
29260         this.fireEvent('download', this);
29261     },
29262     
29263     onTrash : function(e)
29264     {
29265         e.preventDefault();
29266         
29267         this.fireEvent('trash', this);
29268     }
29269     
29270 });
29271 /*
29272  * - LGPL
29273  *
29274  * nav progress bar
29275  * 
29276  */
29277
29278 /**
29279  * @class Roo.bootstrap.NavProgressBar
29280  * @extends Roo.bootstrap.Component
29281  * Bootstrap NavProgressBar class
29282  * 
29283  * @constructor
29284  * Create a new nav progress bar
29285  * @param {Object} config The config object
29286  */
29287
29288 Roo.bootstrap.NavProgressBar = function(config){
29289     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29290
29291     this.bullets = this.bullets || [];
29292    
29293 //    Roo.bootstrap.NavProgressBar.register(this);
29294      this.addEvents({
29295         /**
29296              * @event changed
29297              * Fires when the active item changes
29298              * @param {Roo.bootstrap.NavProgressBar} this
29299              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29300              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29301          */
29302         'changed': true
29303      });
29304     
29305 };
29306
29307 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29308     
29309     bullets : [],
29310     barItems : [],
29311     
29312     getAutoCreate : function()
29313     {
29314         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29315         
29316         cfg = {
29317             tag : 'div',
29318             cls : 'roo-navigation-bar-group',
29319             cn : [
29320                 {
29321                     tag : 'div',
29322                     cls : 'roo-navigation-top-bar'
29323                 },
29324                 {
29325                     tag : 'div',
29326                     cls : 'roo-navigation-bullets-bar',
29327                     cn : [
29328                         {
29329                             tag : 'ul',
29330                             cls : 'roo-navigation-bar'
29331                         }
29332                     ]
29333                 },
29334                 
29335                 {
29336                     tag : 'div',
29337                     cls : 'roo-navigation-bottom-bar'
29338                 }
29339             ]
29340             
29341         };
29342         
29343         return cfg;
29344         
29345     },
29346     
29347     initEvents: function() 
29348     {
29349         
29350     },
29351     
29352     onRender : function(ct, position) 
29353     {
29354         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29355         
29356         if(this.bullets.length){
29357             Roo.each(this.bullets, function(b){
29358                this.addItem(b);
29359             }, this);
29360         }
29361         
29362         this.format();
29363         
29364     },
29365     
29366     addItem : function(cfg)
29367     {
29368         var item = new Roo.bootstrap.NavProgressItem(cfg);
29369         
29370         item.parentId = this.id;
29371         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29372         
29373         if(cfg.html){
29374             var top = new Roo.bootstrap.Element({
29375                 tag : 'div',
29376                 cls : 'roo-navigation-bar-text'
29377             });
29378             
29379             var bottom = new Roo.bootstrap.Element({
29380                 tag : 'div',
29381                 cls : 'roo-navigation-bar-text'
29382             });
29383             
29384             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29385             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29386             
29387             var topText = new Roo.bootstrap.Element({
29388                 tag : 'span',
29389                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29390             });
29391             
29392             var bottomText = new Roo.bootstrap.Element({
29393                 tag : 'span',
29394                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29395             });
29396             
29397             topText.onRender(top.el, null);
29398             bottomText.onRender(bottom.el, null);
29399             
29400             item.topEl = top;
29401             item.bottomEl = bottom;
29402         }
29403         
29404         this.barItems.push(item);
29405         
29406         return item;
29407     },
29408     
29409     getActive : function()
29410     {
29411         var active = false;
29412         
29413         Roo.each(this.barItems, function(v){
29414             
29415             if (!v.isActive()) {
29416                 return;
29417             }
29418             
29419             active = v;
29420             return false;
29421             
29422         });
29423         
29424         return active;
29425     },
29426     
29427     setActiveItem : function(item)
29428     {
29429         var prev = false;
29430         
29431         Roo.each(this.barItems, function(v){
29432             if (v.rid == item.rid) {
29433                 return ;
29434             }
29435             
29436             if (v.isActive()) {
29437                 v.setActive(false);
29438                 prev = v;
29439             }
29440         });
29441
29442         item.setActive(true);
29443         
29444         this.fireEvent('changed', this, item, prev);
29445     },
29446     
29447     getBarItem: function(rid)
29448     {
29449         var ret = false;
29450         
29451         Roo.each(this.barItems, function(e) {
29452             if (e.rid != rid) {
29453                 return;
29454             }
29455             
29456             ret =  e;
29457             return false;
29458         });
29459         
29460         return ret;
29461     },
29462     
29463     indexOfItem : function(item)
29464     {
29465         var index = false;
29466         
29467         Roo.each(this.barItems, function(v, i){
29468             
29469             if (v.rid != item.rid) {
29470                 return;
29471             }
29472             
29473             index = i;
29474             return false
29475         });
29476         
29477         return index;
29478     },
29479     
29480     setActiveNext : function()
29481     {
29482         var i = this.indexOfItem(this.getActive());
29483         
29484         if (i > this.barItems.length) {
29485             return;
29486         }
29487         
29488         this.setActiveItem(this.barItems[i+1]);
29489     },
29490     
29491     setActivePrev : function()
29492     {
29493         var i = this.indexOfItem(this.getActive());
29494         
29495         if (i  < 1) {
29496             return;
29497         }
29498         
29499         this.setActiveItem(this.barItems[i-1]);
29500     },
29501     
29502     format : function()
29503     {
29504         if(!this.barItems.length){
29505             return;
29506         }
29507      
29508         var width = 100 / this.barItems.length;
29509         
29510         Roo.each(this.barItems, function(i){
29511             i.el.setStyle('width', width + '%');
29512             i.topEl.el.setStyle('width', width + '%');
29513             i.bottomEl.el.setStyle('width', width + '%');
29514         }, this);
29515         
29516     }
29517     
29518 });
29519 /*
29520  * - LGPL
29521  *
29522  * Nav Progress Item
29523  * 
29524  */
29525
29526 /**
29527  * @class Roo.bootstrap.NavProgressItem
29528  * @extends Roo.bootstrap.Component
29529  * Bootstrap NavProgressItem class
29530  * @cfg {String} rid the reference id
29531  * @cfg {Boolean} active (true|false) Is item active default false
29532  * @cfg {Boolean} disabled (true|false) Is item active default false
29533  * @cfg {String} html
29534  * @cfg {String} position (top|bottom) text position default bottom
29535  * @cfg {String} icon show icon instead of number
29536  * 
29537  * @constructor
29538  * Create a new NavProgressItem
29539  * @param {Object} config The config object
29540  */
29541 Roo.bootstrap.NavProgressItem = function(config){
29542     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29543     this.addEvents({
29544         // raw events
29545         /**
29546          * @event click
29547          * The raw click event for the entire grid.
29548          * @param {Roo.bootstrap.NavProgressItem} this
29549          * @param {Roo.EventObject} e
29550          */
29551         "click" : true
29552     });
29553    
29554 };
29555
29556 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29557     
29558     rid : '',
29559     active : false,
29560     disabled : false,
29561     html : '',
29562     position : 'bottom',
29563     icon : false,
29564     
29565     getAutoCreate : function()
29566     {
29567         var iconCls = 'roo-navigation-bar-item-icon';
29568         
29569         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29570         
29571         var cfg = {
29572             tag: 'li',
29573             cls: 'roo-navigation-bar-item',
29574             cn : [
29575                 {
29576                     tag : 'i',
29577                     cls : iconCls
29578                 }
29579             ]
29580         };
29581         
29582         if(this.active){
29583             cfg.cls += ' active';
29584         }
29585         if(this.disabled){
29586             cfg.cls += ' disabled';
29587         }
29588         
29589         return cfg;
29590     },
29591     
29592     disable : function()
29593     {
29594         this.setDisabled(true);
29595     },
29596     
29597     enable : function()
29598     {
29599         this.setDisabled(false);
29600     },
29601     
29602     initEvents: function() 
29603     {
29604         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29605         
29606         this.iconEl.on('click', this.onClick, this);
29607     },
29608     
29609     onClick : function(e)
29610     {
29611         e.preventDefault();
29612         
29613         if(this.disabled){
29614             return;
29615         }
29616         
29617         if(this.fireEvent('click', this, e) === false){
29618             return;
29619         };
29620         
29621         this.parent().setActiveItem(this);
29622     },
29623     
29624     isActive: function () 
29625     {
29626         return this.active;
29627     },
29628     
29629     setActive : function(state)
29630     {
29631         if(this.active == state){
29632             return;
29633         }
29634         
29635         this.active = state;
29636         
29637         if (state) {
29638             this.el.addClass('active');
29639             return;
29640         }
29641         
29642         this.el.removeClass('active');
29643         
29644         return;
29645     },
29646     
29647     setDisabled : function(state)
29648     {
29649         if(this.disabled == state){
29650             return;
29651         }
29652         
29653         this.disabled = state;
29654         
29655         if (state) {
29656             this.el.addClass('disabled');
29657             return;
29658         }
29659         
29660         this.el.removeClass('disabled');
29661     },
29662     
29663     tooltipEl : function()
29664     {
29665         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29666     }
29667 });
29668  
29669
29670  /*
29671  * - LGPL
29672  *
29673  * FieldLabel
29674  * 
29675  */
29676
29677 /**
29678  * @class Roo.bootstrap.FieldLabel
29679  * @extends Roo.bootstrap.Component
29680  * Bootstrap FieldLabel class
29681  * @cfg {String} html contents of the element
29682  * @cfg {String} tag tag of the element default label
29683  * @cfg {String} cls class of the element
29684  * @cfg {String} target label target 
29685  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29686  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29687  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29688  * @cfg {String} iconTooltip default "This field is required"
29689  * 
29690  * @constructor
29691  * Create a new FieldLabel
29692  * @param {Object} config The config object
29693  */
29694
29695 Roo.bootstrap.FieldLabel = function(config){
29696     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29697     
29698     this.addEvents({
29699             /**
29700              * @event invalid
29701              * Fires after the field has been marked as invalid.
29702              * @param {Roo.form.FieldLabel} this
29703              * @param {String} msg The validation message
29704              */
29705             invalid : true,
29706             /**
29707              * @event valid
29708              * Fires after the field has been validated with no errors.
29709              * @param {Roo.form.FieldLabel} this
29710              */
29711             valid : true
29712         });
29713 };
29714
29715 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29716     
29717     tag: 'label',
29718     cls: '',
29719     html: '',
29720     target: '',
29721     allowBlank : true,
29722     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29723     validClass : 'text-success fa fa-lg fa-check',
29724     iconTooltip : 'This field is required',
29725     
29726     getAutoCreate : function(){
29727         
29728         var cfg = {
29729             tag : this.tag,
29730             cls : 'roo-bootstrap-field-label ' + this.cls,
29731             for : this.target,
29732             cn : [
29733                 {
29734                     tag : 'i',
29735                     cls : '',
29736                     tooltip : this.iconTooltip
29737                 },
29738                 {
29739                     tag : 'span',
29740                     html : this.html
29741                 }
29742             ] 
29743         };
29744         
29745         return cfg;
29746     },
29747     
29748     initEvents: function() 
29749     {
29750         Roo.bootstrap.Element.superclass.initEvents.call(this);
29751         
29752         this.iconEl = this.el.select('i', true).first();
29753         
29754         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29755         
29756         Roo.bootstrap.FieldLabel.register(this);
29757     },
29758     
29759     /**
29760      * Mark this field as valid
29761      */
29762     markValid : function()
29763     {
29764         this.iconEl.show();
29765         
29766         this.iconEl.removeClass(this.invalidClass);
29767         
29768         this.iconEl.addClass(this.validClass);
29769         
29770         this.fireEvent('valid', this);
29771     },
29772     
29773     /**
29774      * Mark this field as invalid
29775      * @param {String} msg The validation message
29776      */
29777     markInvalid : function(msg)
29778     {
29779         this.iconEl.show();
29780         
29781         this.iconEl.removeClass(this.validClass);
29782         
29783         this.iconEl.addClass(this.invalidClass);
29784         
29785         this.fireEvent('invalid', this, msg);
29786     }
29787     
29788    
29789 });
29790
29791 Roo.apply(Roo.bootstrap.FieldLabel, {
29792     
29793     groups: {},
29794     
29795      /**
29796     * register a FieldLabel Group
29797     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29798     */
29799     register : function(label)
29800     {
29801         if(this.groups.hasOwnProperty(label.target)){
29802             return;
29803         }
29804      
29805         this.groups[label.target] = label;
29806         
29807     },
29808     /**
29809     * fetch a FieldLabel Group based on the target
29810     * @param {string} target
29811     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29812     */
29813     get: function(target) {
29814         if (typeof(this.groups[target]) == 'undefined') {
29815             return false;
29816         }
29817         
29818         return this.groups[target] ;
29819     }
29820 });
29821
29822  
29823
29824  /*
29825  * - LGPL
29826  *
29827  * page DateSplitField.
29828  * 
29829  */
29830
29831
29832 /**
29833  * @class Roo.bootstrap.DateSplitField
29834  * @extends Roo.bootstrap.Component
29835  * Bootstrap DateSplitField class
29836  * @cfg {string} fieldLabel - the label associated
29837  * @cfg {Number} labelWidth set the width of label (0-12)
29838  * @cfg {String} labelAlign (top|left)
29839  * @cfg {Boolean} dayAllowBlank (true|false) default false
29840  * @cfg {Boolean} monthAllowBlank (true|false) default false
29841  * @cfg {Boolean} yearAllowBlank (true|false) default false
29842  * @cfg {string} dayPlaceholder 
29843  * @cfg {string} monthPlaceholder
29844  * @cfg {string} yearPlaceholder
29845  * @cfg {string} dayFormat default 'd'
29846  * @cfg {string} monthFormat default 'm'
29847  * @cfg {string} yearFormat default 'Y'
29848  * @cfg {Number} labellg set the width of label (1-12)
29849  * @cfg {Number} labelmd set the width of label (1-12)
29850  * @cfg {Number} labelsm set the width of label (1-12)
29851  * @cfg {Number} labelxs set the width of label (1-12)
29852
29853  *     
29854  * @constructor
29855  * Create a new DateSplitField
29856  * @param {Object} config The config object
29857  */
29858
29859 Roo.bootstrap.DateSplitField = function(config){
29860     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29861     
29862     this.addEvents({
29863         // raw events
29864          /**
29865          * @event years
29866          * getting the data of years
29867          * @param {Roo.bootstrap.DateSplitField} this
29868          * @param {Object} years
29869          */
29870         "years" : true,
29871         /**
29872          * @event days
29873          * getting the data of days
29874          * @param {Roo.bootstrap.DateSplitField} this
29875          * @param {Object} days
29876          */
29877         "days" : true,
29878         /**
29879          * @event invalid
29880          * Fires after the field has been marked as invalid.
29881          * @param {Roo.form.Field} this
29882          * @param {String} msg The validation message
29883          */
29884         invalid : true,
29885        /**
29886          * @event valid
29887          * Fires after the field has been validated with no errors.
29888          * @param {Roo.form.Field} this
29889          */
29890         valid : true
29891     });
29892 };
29893
29894 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29895     
29896     fieldLabel : '',
29897     labelAlign : 'top',
29898     labelWidth : 3,
29899     dayAllowBlank : false,
29900     monthAllowBlank : false,
29901     yearAllowBlank : false,
29902     dayPlaceholder : '',
29903     monthPlaceholder : '',
29904     yearPlaceholder : '',
29905     dayFormat : 'd',
29906     monthFormat : 'm',
29907     yearFormat : 'Y',
29908     isFormField : true,
29909     labellg : 0,
29910     labelmd : 0,
29911     labelsm : 0,
29912     labelxs : 0,
29913     
29914     getAutoCreate : function()
29915     {
29916         var cfg = {
29917             tag : 'div',
29918             cls : 'row roo-date-split-field-group',
29919             cn : [
29920                 {
29921                     tag : 'input',
29922                     type : 'hidden',
29923                     cls : 'form-hidden-field roo-date-split-field-group-value',
29924                     name : this.name
29925                 }
29926             ]
29927         };
29928         
29929         var labelCls = 'col-md-12';
29930         var contentCls = 'col-md-4';
29931         
29932         if(this.fieldLabel){
29933             
29934             var label = {
29935                 tag : 'div',
29936                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29937                 cn : [
29938                     {
29939                         tag : 'label',
29940                         html : this.fieldLabel
29941                     }
29942                 ]
29943             };
29944             
29945             if(this.labelAlign == 'left'){
29946             
29947                 if(this.labelWidth > 12){
29948                     label.style = "width: " + this.labelWidth + 'px';
29949                 }
29950
29951                 if(this.labelWidth < 13 && this.labelmd == 0){
29952                     this.labelmd = this.labelWidth;
29953                 }
29954
29955                 if(this.labellg > 0){
29956                     labelCls = ' col-lg-' + this.labellg;
29957                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29958                 }
29959
29960                 if(this.labelmd > 0){
29961                     labelCls = ' col-md-' + this.labelmd;
29962                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29963                 }
29964
29965                 if(this.labelsm > 0){
29966                     labelCls = ' col-sm-' + this.labelsm;
29967                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29968                 }
29969
29970                 if(this.labelxs > 0){
29971                     labelCls = ' col-xs-' + this.labelxs;
29972                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29973                 }
29974             }
29975             
29976             label.cls += ' ' + labelCls;
29977             
29978             cfg.cn.push(label);
29979         }
29980         
29981         Roo.each(['day', 'month', 'year'], function(t){
29982             cfg.cn.push({
29983                 tag : 'div',
29984                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29985             });
29986         }, this);
29987         
29988         return cfg;
29989     },
29990     
29991     inputEl: function ()
29992     {
29993         return this.el.select('.roo-date-split-field-group-value', true).first();
29994     },
29995     
29996     onRender : function(ct, position) 
29997     {
29998         var _this = this;
29999         
30000         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30001         
30002         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30003         
30004         this.dayField = new Roo.bootstrap.ComboBox({
30005             allowBlank : this.dayAllowBlank,
30006             alwaysQuery : true,
30007             displayField : 'value',
30008             editable : false,
30009             fieldLabel : '',
30010             forceSelection : true,
30011             mode : 'local',
30012             placeholder : this.dayPlaceholder,
30013             selectOnFocus : true,
30014             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30015             triggerAction : 'all',
30016             typeAhead : true,
30017             valueField : 'value',
30018             store : new Roo.data.SimpleStore({
30019                 data : (function() {    
30020                     var days = [];
30021                     _this.fireEvent('days', _this, days);
30022                     return days;
30023                 })(),
30024                 fields : [ 'value' ]
30025             }),
30026             listeners : {
30027                 select : function (_self, record, index)
30028                 {
30029                     _this.setValue(_this.getValue());
30030                 }
30031             }
30032         });
30033
30034         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30035         
30036         this.monthField = new Roo.bootstrap.MonthField({
30037             after : '<i class=\"fa fa-calendar\"></i>',
30038             allowBlank : this.monthAllowBlank,
30039             placeholder : this.monthPlaceholder,
30040             readOnly : true,
30041             listeners : {
30042                 render : function (_self)
30043                 {
30044                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30045                         e.preventDefault();
30046                         _self.focus();
30047                     });
30048                 },
30049                 select : function (_self, oldvalue, newvalue)
30050                 {
30051                     _this.setValue(_this.getValue());
30052                 }
30053             }
30054         });
30055         
30056         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30057         
30058         this.yearField = new Roo.bootstrap.ComboBox({
30059             allowBlank : this.yearAllowBlank,
30060             alwaysQuery : true,
30061             displayField : 'value',
30062             editable : false,
30063             fieldLabel : '',
30064             forceSelection : true,
30065             mode : 'local',
30066             placeholder : this.yearPlaceholder,
30067             selectOnFocus : true,
30068             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30069             triggerAction : 'all',
30070             typeAhead : true,
30071             valueField : 'value',
30072             store : new Roo.data.SimpleStore({
30073                 data : (function() {
30074                     var years = [];
30075                     _this.fireEvent('years', _this, years);
30076                     return years;
30077                 })(),
30078                 fields : [ 'value' ]
30079             }),
30080             listeners : {
30081                 select : function (_self, record, index)
30082                 {
30083                     _this.setValue(_this.getValue());
30084                 }
30085             }
30086         });
30087
30088         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30089     },
30090     
30091     setValue : function(v, format)
30092     {
30093         this.inputEl.dom.value = v;
30094         
30095         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30096         
30097         var d = Date.parseDate(v, f);
30098         
30099         if(!d){
30100             this.validate();
30101             return;
30102         }
30103         
30104         this.setDay(d.format(this.dayFormat));
30105         this.setMonth(d.format(this.monthFormat));
30106         this.setYear(d.format(this.yearFormat));
30107         
30108         this.validate();
30109         
30110         return;
30111     },
30112     
30113     setDay : function(v)
30114     {
30115         this.dayField.setValue(v);
30116         this.inputEl.dom.value = this.getValue();
30117         this.validate();
30118         return;
30119     },
30120     
30121     setMonth : function(v)
30122     {
30123         this.monthField.setValue(v, true);
30124         this.inputEl.dom.value = this.getValue();
30125         this.validate();
30126         return;
30127     },
30128     
30129     setYear : function(v)
30130     {
30131         this.yearField.setValue(v);
30132         this.inputEl.dom.value = this.getValue();
30133         this.validate();
30134         return;
30135     },
30136     
30137     getDay : function()
30138     {
30139         return this.dayField.getValue();
30140     },
30141     
30142     getMonth : function()
30143     {
30144         return this.monthField.getValue();
30145     },
30146     
30147     getYear : function()
30148     {
30149         return this.yearField.getValue();
30150     },
30151     
30152     getValue : function()
30153     {
30154         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30155         
30156         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30157         
30158         return date;
30159     },
30160     
30161     reset : function()
30162     {
30163         this.setDay('');
30164         this.setMonth('');
30165         this.setYear('');
30166         this.inputEl.dom.value = '';
30167         this.validate();
30168         return;
30169     },
30170     
30171     validate : function()
30172     {
30173         var d = this.dayField.validate();
30174         var m = this.monthField.validate();
30175         var y = this.yearField.validate();
30176         
30177         var valid = true;
30178         
30179         if(
30180                 (!this.dayAllowBlank && !d) ||
30181                 (!this.monthAllowBlank && !m) ||
30182                 (!this.yearAllowBlank && !y)
30183         ){
30184             valid = false;
30185         }
30186         
30187         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30188             return valid;
30189         }
30190         
30191         if(valid){
30192             this.markValid();
30193             return valid;
30194         }
30195         
30196         this.markInvalid();
30197         
30198         return valid;
30199     },
30200     
30201     markValid : function()
30202     {
30203         
30204         var label = this.el.select('label', true).first();
30205         var icon = this.el.select('i.fa-star', true).first();
30206
30207         if(label && icon){
30208             icon.remove();
30209         }
30210         
30211         this.fireEvent('valid', this);
30212     },
30213     
30214      /**
30215      * Mark this field as invalid
30216      * @param {String} msg The validation message
30217      */
30218     markInvalid : function(msg)
30219     {
30220         
30221         var label = this.el.select('label', true).first();
30222         var icon = this.el.select('i.fa-star', true).first();
30223
30224         if(label && !icon){
30225             this.el.select('.roo-date-split-field-label', true).createChild({
30226                 tag : 'i',
30227                 cls : 'text-danger fa fa-lg fa-star',
30228                 tooltip : 'This field is required',
30229                 style : 'margin-right:5px;'
30230             }, label, true);
30231         }
30232         
30233         this.fireEvent('invalid', this, msg);
30234     },
30235     
30236     clearInvalid : function()
30237     {
30238         var label = this.el.select('label', true).first();
30239         var icon = this.el.select('i.fa-star', true).first();
30240
30241         if(label && icon){
30242             icon.remove();
30243         }
30244         
30245         this.fireEvent('valid', this);
30246     },
30247     
30248     getName: function()
30249     {
30250         return this.name;
30251     }
30252     
30253 });
30254
30255  /**
30256  *
30257  * This is based on 
30258  * http://masonry.desandro.com
30259  *
30260  * The idea is to render all the bricks based on vertical width...
30261  *
30262  * The original code extends 'outlayer' - we might need to use that....
30263  * 
30264  */
30265
30266
30267 /**
30268  * @class Roo.bootstrap.LayoutMasonry
30269  * @extends Roo.bootstrap.Component
30270  * Bootstrap Layout Masonry class
30271  * 
30272  * @constructor
30273  * Create a new Element
30274  * @param {Object} config The config object
30275  */
30276
30277 Roo.bootstrap.LayoutMasonry = function(config){
30278     
30279     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30280     
30281     this.bricks = [];
30282     
30283     Roo.bootstrap.LayoutMasonry.register(this);
30284     
30285     this.addEvents({
30286         // raw events
30287         /**
30288          * @event layout
30289          * Fire after layout the items
30290          * @param {Roo.bootstrap.LayoutMasonry} this
30291          * @param {Roo.EventObject} e
30292          */
30293         "layout" : true
30294     });
30295     
30296 };
30297
30298 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30299     
30300     /**
30301      * @cfg {Boolean} isLayoutInstant = no animation?
30302      */   
30303     isLayoutInstant : false, // needed?
30304    
30305     /**
30306      * @cfg {Number} boxWidth  width of the columns
30307      */   
30308     boxWidth : 450,
30309     
30310       /**
30311      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30312      */   
30313     boxHeight : 0,
30314     
30315     /**
30316      * @cfg {Number} padWidth padding below box..
30317      */   
30318     padWidth : 10, 
30319     
30320     /**
30321      * @cfg {Number} gutter gutter width..
30322      */   
30323     gutter : 10,
30324     
30325      /**
30326      * @cfg {Number} maxCols maximum number of columns
30327      */   
30328     
30329     maxCols: 0,
30330     
30331     /**
30332      * @cfg {Boolean} isAutoInitial defalut true
30333      */   
30334     isAutoInitial : true, 
30335     
30336     containerWidth: 0,
30337     
30338     /**
30339      * @cfg {Boolean} isHorizontal defalut false
30340      */   
30341     isHorizontal : false, 
30342
30343     currentSize : null,
30344     
30345     tag: 'div',
30346     
30347     cls: '',
30348     
30349     bricks: null, //CompositeElement
30350     
30351     cols : 1,
30352     
30353     _isLayoutInited : false,
30354     
30355 //    isAlternative : false, // only use for vertical layout...
30356     
30357     /**
30358      * @cfg {Number} alternativePadWidth padding below box..
30359      */   
30360     alternativePadWidth : 50,
30361     
30362     selectedBrick : [],
30363     
30364     getAutoCreate : function(){
30365         
30366         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30367         
30368         var cfg = {
30369             tag: this.tag,
30370             cls: 'blog-masonary-wrapper ' + this.cls,
30371             cn : {
30372                 cls : 'mas-boxes masonary'
30373             }
30374         };
30375         
30376         return cfg;
30377     },
30378     
30379     getChildContainer: function( )
30380     {
30381         if (this.boxesEl) {
30382             return this.boxesEl;
30383         }
30384         
30385         this.boxesEl = this.el.select('.mas-boxes').first();
30386         
30387         return this.boxesEl;
30388     },
30389     
30390     
30391     initEvents : function()
30392     {
30393         var _this = this;
30394         
30395         if(this.isAutoInitial){
30396             Roo.log('hook children rendered');
30397             this.on('childrenrendered', function() {
30398                 Roo.log('children rendered');
30399                 _this.initial();
30400             } ,this);
30401         }
30402     },
30403     
30404     initial : function()
30405     {
30406         this.selectedBrick = [];
30407         
30408         this.currentSize = this.el.getBox(true);
30409         
30410         Roo.EventManager.onWindowResize(this.resize, this); 
30411
30412         if(!this.isAutoInitial){
30413             this.layout();
30414             return;
30415         }
30416         
30417         this.layout();
30418         
30419         return;
30420         //this.layout.defer(500,this);
30421         
30422     },
30423     
30424     resize : function()
30425     {
30426         var cs = this.el.getBox(true);
30427         
30428         if (
30429                 this.currentSize.width == cs.width && 
30430                 this.currentSize.x == cs.x && 
30431                 this.currentSize.height == cs.height && 
30432                 this.currentSize.y == cs.y 
30433         ) {
30434             Roo.log("no change in with or X or Y");
30435             return;
30436         }
30437         
30438         this.currentSize = cs;
30439         
30440         this.layout();
30441         
30442     },
30443     
30444     layout : function()
30445     {   
30446         this._resetLayout();
30447         
30448         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30449         
30450         this.layoutItems( isInstant );
30451       
30452         this._isLayoutInited = true;
30453         
30454         this.fireEvent('layout', this);
30455         
30456     },
30457     
30458     _resetLayout : function()
30459     {
30460         if(this.isHorizontal){
30461             this.horizontalMeasureColumns();
30462             return;
30463         }
30464         
30465         this.verticalMeasureColumns();
30466         
30467     },
30468     
30469     verticalMeasureColumns : function()
30470     {
30471         this.getContainerWidth();
30472         
30473 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30474 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30475 //            return;
30476 //        }
30477         
30478         var boxWidth = this.boxWidth + this.padWidth;
30479         
30480         if(this.containerWidth < this.boxWidth){
30481             boxWidth = this.containerWidth
30482         }
30483         
30484         var containerWidth = this.containerWidth;
30485         
30486         var cols = Math.floor(containerWidth / boxWidth);
30487         
30488         this.cols = Math.max( cols, 1 );
30489         
30490         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30491         
30492         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30493         
30494         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30495         
30496         this.colWidth = boxWidth + avail - this.padWidth;
30497         
30498         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30499         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30500     },
30501     
30502     horizontalMeasureColumns : function()
30503     {
30504         this.getContainerWidth();
30505         
30506         var boxWidth = this.boxWidth;
30507         
30508         if(this.containerWidth < boxWidth){
30509             boxWidth = this.containerWidth;
30510         }
30511         
30512         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30513         
30514         this.el.setHeight(boxWidth);
30515         
30516     },
30517     
30518     getContainerWidth : function()
30519     {
30520         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30521     },
30522     
30523     layoutItems : function( isInstant )
30524     {
30525         Roo.log(this.bricks);
30526         
30527         var items = Roo.apply([], this.bricks);
30528         
30529         if(this.isHorizontal){
30530             this._horizontalLayoutItems( items , isInstant );
30531             return;
30532         }
30533         
30534 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30535 //            this._verticalAlternativeLayoutItems( items , isInstant );
30536 //            return;
30537 //        }
30538         
30539         this._verticalLayoutItems( items , isInstant );
30540         
30541     },
30542     
30543     _verticalLayoutItems : function ( items , isInstant)
30544     {
30545         if ( !items || !items.length ) {
30546             return;
30547         }
30548         
30549         var standard = [
30550             ['xs', 'xs', 'xs', 'tall'],
30551             ['xs', 'xs', 'tall'],
30552             ['xs', 'xs', 'sm'],
30553             ['xs', 'xs', 'xs'],
30554             ['xs', 'tall'],
30555             ['xs', 'sm'],
30556             ['xs', 'xs'],
30557             ['xs'],
30558             
30559             ['sm', 'xs', 'xs'],
30560             ['sm', 'xs'],
30561             ['sm'],
30562             
30563             ['tall', 'xs', 'xs', 'xs'],
30564             ['tall', 'xs', 'xs'],
30565             ['tall', 'xs'],
30566             ['tall']
30567             
30568         ];
30569         
30570         var queue = [];
30571         
30572         var boxes = [];
30573         
30574         var box = [];
30575         
30576         Roo.each(items, function(item, k){
30577             
30578             switch (item.size) {
30579                 // these layouts take up a full box,
30580                 case 'md' :
30581                 case 'md-left' :
30582                 case 'md-right' :
30583                 case 'wide' :
30584                     
30585                     if(box.length){
30586                         boxes.push(box);
30587                         box = [];
30588                     }
30589                     
30590                     boxes.push([item]);
30591                     
30592                     break;
30593                     
30594                 case 'xs' :
30595                 case 'sm' :
30596                 case 'tall' :
30597                     
30598                     box.push(item);
30599                     
30600                     break;
30601                 default :
30602                     break;
30603                     
30604             }
30605             
30606         }, this);
30607         
30608         if(box.length){
30609             boxes.push(box);
30610             box = [];
30611         }
30612         
30613         var filterPattern = function(box, length)
30614         {
30615             if(!box.length){
30616                 return;
30617             }
30618             
30619             var match = false;
30620             
30621             var pattern = box.slice(0, length);
30622             
30623             var format = [];
30624             
30625             Roo.each(pattern, function(i){
30626                 format.push(i.size);
30627             }, this);
30628             
30629             Roo.each(standard, function(s){
30630                 
30631                 if(String(s) != String(format)){
30632                     return;
30633                 }
30634                 
30635                 match = true;
30636                 return false;
30637                 
30638             }, this);
30639             
30640             if(!match && length == 1){
30641                 return;
30642             }
30643             
30644             if(!match){
30645                 filterPattern(box, length - 1);
30646                 return;
30647             }
30648                 
30649             queue.push(pattern);
30650
30651             box = box.slice(length, box.length);
30652
30653             filterPattern(box, 4);
30654
30655             return;
30656             
30657         }
30658         
30659         Roo.each(boxes, function(box, k){
30660             
30661             if(!box.length){
30662                 return;
30663             }
30664             
30665             if(box.length == 1){
30666                 queue.push(box);
30667                 return;
30668             }
30669             
30670             filterPattern(box, 4);
30671             
30672         }, this);
30673         
30674         this._processVerticalLayoutQueue( queue, isInstant );
30675         
30676     },
30677     
30678 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30679 //    {
30680 //        if ( !items || !items.length ) {
30681 //            return;
30682 //        }
30683 //
30684 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30685 //        
30686 //    },
30687     
30688     _horizontalLayoutItems : function ( items , isInstant)
30689     {
30690         if ( !items || !items.length || items.length < 3) {
30691             return;
30692         }
30693         
30694         items.reverse();
30695         
30696         var eItems = items.slice(0, 3);
30697         
30698         items = items.slice(3, items.length);
30699         
30700         var standard = [
30701             ['xs', 'xs', 'xs', 'wide'],
30702             ['xs', 'xs', 'wide'],
30703             ['xs', 'xs', 'sm'],
30704             ['xs', 'xs', 'xs'],
30705             ['xs', 'wide'],
30706             ['xs', 'sm'],
30707             ['xs', 'xs'],
30708             ['xs'],
30709             
30710             ['sm', 'xs', 'xs'],
30711             ['sm', 'xs'],
30712             ['sm'],
30713             
30714             ['wide', 'xs', 'xs', 'xs'],
30715             ['wide', 'xs', 'xs'],
30716             ['wide', 'xs'],
30717             ['wide'],
30718             
30719             ['wide-thin']
30720         ];
30721         
30722         var queue = [];
30723         
30724         var boxes = [];
30725         
30726         var box = [];
30727         
30728         Roo.each(items, function(item, k){
30729             
30730             switch (item.size) {
30731                 case 'md' :
30732                 case 'md-left' :
30733                 case 'md-right' :
30734                 case 'tall' :
30735                     
30736                     if(box.length){
30737                         boxes.push(box);
30738                         box = [];
30739                     }
30740                     
30741                     boxes.push([item]);
30742                     
30743                     break;
30744                     
30745                 case 'xs' :
30746                 case 'sm' :
30747                 case 'wide' :
30748                 case 'wide-thin' :
30749                     
30750                     box.push(item);
30751                     
30752                     break;
30753                 default :
30754                     break;
30755                     
30756             }
30757             
30758         }, this);
30759         
30760         if(box.length){
30761             boxes.push(box);
30762             box = [];
30763         }
30764         
30765         var filterPattern = function(box, length)
30766         {
30767             if(!box.length){
30768                 return;
30769             }
30770             
30771             var match = false;
30772             
30773             var pattern = box.slice(0, length);
30774             
30775             var format = [];
30776             
30777             Roo.each(pattern, function(i){
30778                 format.push(i.size);
30779             }, this);
30780             
30781             Roo.each(standard, function(s){
30782                 
30783                 if(String(s) != String(format)){
30784                     return;
30785                 }
30786                 
30787                 match = true;
30788                 return false;
30789                 
30790             }, this);
30791             
30792             if(!match && length == 1){
30793                 return;
30794             }
30795             
30796             if(!match){
30797                 filterPattern(box, length - 1);
30798                 return;
30799             }
30800                 
30801             queue.push(pattern);
30802
30803             box = box.slice(length, box.length);
30804
30805             filterPattern(box, 4);
30806
30807             return;
30808             
30809         }
30810         
30811         Roo.each(boxes, function(box, k){
30812             
30813             if(!box.length){
30814                 return;
30815             }
30816             
30817             if(box.length == 1){
30818                 queue.push(box);
30819                 return;
30820             }
30821             
30822             filterPattern(box, 4);
30823             
30824         }, this);
30825         
30826         
30827         var prune = [];
30828         
30829         var pos = this.el.getBox(true);
30830         
30831         var minX = pos.x;
30832         
30833         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30834         
30835         var hit_end = false;
30836         
30837         Roo.each(queue, function(box){
30838             
30839             if(hit_end){
30840                 
30841                 Roo.each(box, function(b){
30842                 
30843                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30844                     b.el.hide();
30845
30846                 }, this);
30847
30848                 return;
30849             }
30850             
30851             var mx = 0;
30852             
30853             Roo.each(box, function(b){
30854                 
30855                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30856                 b.el.show();
30857
30858                 mx = Math.max(mx, b.x);
30859                 
30860             }, this);
30861             
30862             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30863             
30864             if(maxX < minX){
30865                 
30866                 Roo.each(box, function(b){
30867                 
30868                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30869                     b.el.hide();
30870                     
30871                 }, this);
30872                 
30873                 hit_end = true;
30874                 
30875                 return;
30876             }
30877             
30878             prune.push(box);
30879             
30880         }, this);
30881         
30882         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30883     },
30884     
30885     /** Sets position of item in DOM
30886     * @param {Element} item
30887     * @param {Number} x - horizontal position
30888     * @param {Number} y - vertical position
30889     * @param {Boolean} isInstant - disables transitions
30890     */
30891     _processVerticalLayoutQueue : function( queue, isInstant )
30892     {
30893         var pos = this.el.getBox(true);
30894         var x = pos.x;
30895         var y = pos.y;
30896         var maxY = [];
30897         
30898         for (var i = 0; i < this.cols; i++){
30899             maxY[i] = pos.y;
30900         }
30901         
30902         Roo.each(queue, function(box, k){
30903             
30904             var col = k % this.cols;
30905             
30906             Roo.each(box, function(b,kk){
30907                 
30908                 b.el.position('absolute');
30909                 
30910                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30911                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30912                 
30913                 if(b.size == 'md-left' || b.size == 'md-right'){
30914                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30915                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30916                 }
30917                 
30918                 b.el.setWidth(width);
30919                 b.el.setHeight(height);
30920                 // iframe?
30921                 b.el.select('iframe',true).setSize(width,height);
30922                 
30923             }, this);
30924             
30925             for (var i = 0; i < this.cols; i++){
30926                 
30927                 if(maxY[i] < maxY[col]){
30928                     col = i;
30929                     continue;
30930                 }
30931                 
30932                 col = Math.min(col, i);
30933                 
30934             }
30935             
30936             x = pos.x + col * (this.colWidth + this.padWidth);
30937             
30938             y = maxY[col];
30939             
30940             var positions = [];
30941             
30942             switch (box.length){
30943                 case 1 :
30944                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30945                     break;
30946                 case 2 :
30947                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30948                     break;
30949                 case 3 :
30950                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30951                     break;
30952                 case 4 :
30953                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30954                     break;
30955                 default :
30956                     break;
30957             }
30958             
30959             Roo.each(box, function(b,kk){
30960                 
30961                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30962                 
30963                 var sz = b.el.getSize();
30964                 
30965                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30966                 
30967             }, this);
30968             
30969         }, this);
30970         
30971         var mY = 0;
30972         
30973         for (var i = 0; i < this.cols; i++){
30974             mY = Math.max(mY, maxY[i]);
30975         }
30976         
30977         this.el.setHeight(mY - pos.y);
30978         
30979     },
30980     
30981 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30982 //    {
30983 //        var pos = this.el.getBox(true);
30984 //        var x = pos.x;
30985 //        var y = pos.y;
30986 //        var maxX = pos.right;
30987 //        
30988 //        var maxHeight = 0;
30989 //        
30990 //        Roo.each(items, function(item, k){
30991 //            
30992 //            var c = k % 2;
30993 //            
30994 //            item.el.position('absolute');
30995 //                
30996 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30997 //
30998 //            item.el.setWidth(width);
30999 //
31000 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31001 //
31002 //            item.el.setHeight(height);
31003 //            
31004 //            if(c == 0){
31005 //                item.el.setXY([x, y], isInstant ? false : true);
31006 //            } else {
31007 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31008 //            }
31009 //            
31010 //            y = y + height + this.alternativePadWidth;
31011 //            
31012 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31013 //            
31014 //        }, this);
31015 //        
31016 //        this.el.setHeight(maxHeight);
31017 //        
31018 //    },
31019     
31020     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31021     {
31022         var pos = this.el.getBox(true);
31023         
31024         var minX = pos.x;
31025         var minY = pos.y;
31026         
31027         var maxX = pos.right;
31028         
31029         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31030         
31031         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31032         
31033         Roo.each(queue, function(box, k){
31034             
31035             Roo.each(box, function(b, kk){
31036                 
31037                 b.el.position('absolute');
31038                 
31039                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31040                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31041                 
31042                 if(b.size == 'md-left' || b.size == 'md-right'){
31043                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31044                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31045                 }
31046                 
31047                 b.el.setWidth(width);
31048                 b.el.setHeight(height);
31049                 
31050             }, this);
31051             
31052             if(!box.length){
31053                 return;
31054             }
31055             
31056             var positions = [];
31057             
31058             switch (box.length){
31059                 case 1 :
31060                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31061                     break;
31062                 case 2 :
31063                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31064                     break;
31065                 case 3 :
31066                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31067                     break;
31068                 case 4 :
31069                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31070                     break;
31071                 default :
31072                     break;
31073             }
31074             
31075             Roo.each(box, function(b,kk){
31076                 
31077                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31078                 
31079                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31080                 
31081             }, this);
31082             
31083         }, this);
31084         
31085     },
31086     
31087     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31088     {
31089         Roo.each(eItems, function(b,k){
31090             
31091             b.size = (k == 0) ? 'sm' : 'xs';
31092             b.x = (k == 0) ? 2 : 1;
31093             b.y = (k == 0) ? 2 : 1;
31094             
31095             b.el.position('absolute');
31096             
31097             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31098                 
31099             b.el.setWidth(width);
31100             
31101             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31102             
31103             b.el.setHeight(height);
31104             
31105         }, this);
31106
31107         var positions = [];
31108         
31109         positions.push({
31110             x : maxX - this.unitWidth * 2 - this.gutter,
31111             y : minY
31112         });
31113         
31114         positions.push({
31115             x : maxX - this.unitWidth,
31116             y : minY + (this.unitWidth + this.gutter) * 2
31117         });
31118         
31119         positions.push({
31120             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31121             y : minY
31122         });
31123         
31124         Roo.each(eItems, function(b,k){
31125             
31126             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31127
31128         }, this);
31129         
31130     },
31131     
31132     getVerticalOneBoxColPositions : function(x, y, box)
31133     {
31134         var pos = [];
31135         
31136         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31137         
31138         if(box[0].size == 'md-left'){
31139             rand = 0;
31140         }
31141         
31142         if(box[0].size == 'md-right'){
31143             rand = 1;
31144         }
31145         
31146         pos.push({
31147             x : x + (this.unitWidth + this.gutter) * rand,
31148             y : y
31149         });
31150         
31151         return pos;
31152     },
31153     
31154     getVerticalTwoBoxColPositions : function(x, y, box)
31155     {
31156         var pos = [];
31157         
31158         if(box[0].size == 'xs'){
31159             
31160             pos.push({
31161                 x : x,
31162                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31163             });
31164
31165             pos.push({
31166                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31167                 y : y
31168             });
31169             
31170             return pos;
31171             
31172         }
31173         
31174         pos.push({
31175             x : x,
31176             y : y
31177         });
31178
31179         pos.push({
31180             x : x + (this.unitWidth + this.gutter) * 2,
31181             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31182         });
31183         
31184         return pos;
31185         
31186     },
31187     
31188     getVerticalThreeBoxColPositions : function(x, y, box)
31189     {
31190         var pos = [];
31191         
31192         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31193             
31194             pos.push({
31195                 x : x,
31196                 y : y
31197             });
31198
31199             pos.push({
31200                 x : x + (this.unitWidth + this.gutter) * 1,
31201                 y : y
31202             });
31203             
31204             pos.push({
31205                 x : x + (this.unitWidth + this.gutter) * 2,
31206                 y : y
31207             });
31208             
31209             return pos;
31210             
31211         }
31212         
31213         if(box[0].size == 'xs' && box[1].size == 'xs'){
31214             
31215             pos.push({
31216                 x : x,
31217                 y : y
31218             });
31219
31220             pos.push({
31221                 x : x,
31222                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31223             });
31224             
31225             pos.push({
31226                 x : x + (this.unitWidth + this.gutter) * 1,
31227                 y : y
31228             });
31229             
31230             return pos;
31231             
31232         }
31233         
31234         pos.push({
31235             x : x,
31236             y : y
31237         });
31238
31239         pos.push({
31240             x : x + (this.unitWidth + this.gutter) * 2,
31241             y : y
31242         });
31243
31244         pos.push({
31245             x : x + (this.unitWidth + this.gutter) * 2,
31246             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31247         });
31248             
31249         return pos;
31250         
31251     },
31252     
31253     getVerticalFourBoxColPositions : function(x, y, box)
31254     {
31255         var pos = [];
31256         
31257         if(box[0].size == 'xs'){
31258             
31259             pos.push({
31260                 x : x,
31261                 y : y
31262             });
31263
31264             pos.push({
31265                 x : x,
31266                 y : y + (this.unitHeight + this.gutter) * 1
31267             });
31268             
31269             pos.push({
31270                 x : x,
31271                 y : y + (this.unitHeight + this.gutter) * 2
31272             });
31273             
31274             pos.push({
31275                 x : x + (this.unitWidth + this.gutter) * 1,
31276                 y : y
31277             });
31278             
31279             return pos;
31280             
31281         }
31282         
31283         pos.push({
31284             x : x,
31285             y : y
31286         });
31287
31288         pos.push({
31289             x : x + (this.unitWidth + this.gutter) * 2,
31290             y : y
31291         });
31292
31293         pos.push({
31294             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31295             y : y + (this.unitHeight + this.gutter) * 1
31296         });
31297
31298         pos.push({
31299             x : x + (this.unitWidth + this.gutter) * 2,
31300             y : y + (this.unitWidth + this.gutter) * 2
31301         });
31302
31303         return pos;
31304         
31305     },
31306     
31307     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31308     {
31309         var pos = [];
31310         
31311         if(box[0].size == 'md-left'){
31312             pos.push({
31313                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31314                 y : minY
31315             });
31316             
31317             return pos;
31318         }
31319         
31320         if(box[0].size == 'md-right'){
31321             pos.push({
31322                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31323                 y : minY + (this.unitWidth + this.gutter) * 1
31324             });
31325             
31326             return pos;
31327         }
31328         
31329         var rand = Math.floor(Math.random() * (4 - box[0].y));
31330         
31331         pos.push({
31332             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31333             y : minY + (this.unitWidth + this.gutter) * rand
31334         });
31335         
31336         return pos;
31337         
31338     },
31339     
31340     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31341     {
31342         var pos = [];
31343         
31344         if(box[0].size == 'xs'){
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) * (3 - box[1].y)
31354             });
31355             
31356             return pos;
31357             
31358         }
31359         
31360         pos.push({
31361             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31362             y : minY
31363         });
31364
31365         pos.push({
31366             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31367             y : minY + (this.unitWidth + this.gutter) * 2
31368         });
31369         
31370         return pos;
31371         
31372     },
31373     
31374     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31375     {
31376         var pos = [];
31377         
31378         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31379             
31380             pos.push({
31381                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31382                 y : minY
31383             });
31384
31385             pos.push({
31386                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31387                 y : minY + (this.unitWidth + this.gutter) * 1
31388             });
31389             
31390             pos.push({
31391                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31392                 y : minY + (this.unitWidth + this.gutter) * 2
31393             });
31394             
31395             return pos;
31396             
31397         }
31398         
31399         if(box[0].size == 'xs' && box[1].size == 'xs'){
31400             
31401             pos.push({
31402                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31403                 y : minY
31404             });
31405
31406             pos.push({
31407                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31408                 y : minY
31409             });
31410             
31411             pos.push({
31412                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31413                 y : minY + (this.unitWidth + this.gutter) * 1
31414             });
31415             
31416             return pos;
31417             
31418         }
31419         
31420         pos.push({
31421             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31422             y : minY
31423         });
31424
31425         pos.push({
31426             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31427             y : minY + (this.unitWidth + this.gutter) * 2
31428         });
31429
31430         pos.push({
31431             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31432             y : minY + (this.unitWidth + this.gutter) * 2
31433         });
31434             
31435         return pos;
31436         
31437     },
31438     
31439     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31440     {
31441         var pos = [];
31442         
31443         if(box[0].size == 'xs'){
31444             
31445             pos.push({
31446                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31447                 y : minY
31448             });
31449
31450             pos.push({
31451                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31452                 y : minY
31453             });
31454             
31455             pos.push({
31456                 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),
31457                 y : minY
31458             });
31459             
31460             pos.push({
31461                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31462                 y : minY + (this.unitWidth + this.gutter) * 1
31463             });
31464             
31465             return pos;
31466             
31467         }
31468         
31469         pos.push({
31470             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31471             y : minY
31472         });
31473         
31474         pos.push({
31475             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31476             y : minY + (this.unitWidth + this.gutter) * 2
31477         });
31478         
31479         pos.push({
31480             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31481             y : minY + (this.unitWidth + this.gutter) * 2
31482         });
31483         
31484         pos.push({
31485             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),
31486             y : minY + (this.unitWidth + this.gutter) * 2
31487         });
31488
31489         return pos;
31490         
31491     },
31492     
31493     /**
31494     * remove a Masonry Brick
31495     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31496     */
31497     removeBrick : function(brick_id)
31498     {
31499         if (!brick_id) {
31500             return;
31501         }
31502         
31503         for (var i = 0; i<this.bricks.length; i++) {
31504             if (this.bricks[i].id == brick_id) {
31505                 this.bricks.splice(i,1);
31506                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31507                 this.initial();
31508             }
31509         }
31510     },
31511     
31512     /**
31513     * adds a Masonry Brick
31514     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31515     */
31516     addBrick : function(cfg)
31517     {
31518         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31519         //this.register(cn);
31520         cn.parentId = this.id;
31521         cn.onRender(this.el, null);
31522         return cn;
31523     },
31524     
31525     /**
31526     * register a Masonry Brick
31527     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31528     */
31529     
31530     register : function(brick)
31531     {
31532         this.bricks.push(brick);
31533         brick.masonryId = this.id;
31534     },
31535     
31536     /**
31537     * clear all the Masonry Brick
31538     */
31539     clearAll : function()
31540     {
31541         this.bricks = [];
31542         //this.getChildContainer().dom.innerHTML = "";
31543         this.el.dom.innerHTML = '';
31544     },
31545     
31546     getSelected : function()
31547     {
31548         if (!this.selectedBrick) {
31549             return false;
31550         }
31551         
31552         return this.selectedBrick;
31553     }
31554 });
31555
31556 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31557     
31558     groups: {},
31559      /**
31560     * register a Masonry Layout
31561     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31562     */
31563     
31564     register : function(layout)
31565     {
31566         this.groups[layout.id] = layout;
31567     },
31568     /**
31569     * fetch a  Masonry Layout based on the masonry layout ID
31570     * @param {string} the masonry layout to add
31571     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31572     */
31573     
31574     get: function(layout_id) {
31575         if (typeof(this.groups[layout_id]) == 'undefined') {
31576             return false;
31577         }
31578         return this.groups[layout_id] ;
31579     }
31580     
31581     
31582     
31583 });
31584
31585  
31586
31587  /**
31588  *
31589  * This is based on 
31590  * http://masonry.desandro.com
31591  *
31592  * The idea is to render all the bricks based on vertical width...
31593  *
31594  * The original code extends 'outlayer' - we might need to use that....
31595  * 
31596  */
31597
31598
31599 /**
31600  * @class Roo.bootstrap.LayoutMasonryAuto
31601  * @extends Roo.bootstrap.Component
31602  * Bootstrap Layout Masonry class
31603  * 
31604  * @constructor
31605  * Create a new Element
31606  * @param {Object} config The config object
31607  */
31608
31609 Roo.bootstrap.LayoutMasonryAuto = function(config){
31610     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31611 };
31612
31613 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31614     
31615       /**
31616      * @cfg {Boolean} isFitWidth  - resize the width..
31617      */   
31618     isFitWidth : false,  // options..
31619     /**
31620      * @cfg {Boolean} isOriginLeft = left align?
31621      */   
31622     isOriginLeft : true,
31623     /**
31624      * @cfg {Boolean} isOriginTop = top align?
31625      */   
31626     isOriginTop : false,
31627     /**
31628      * @cfg {Boolean} isLayoutInstant = no animation?
31629      */   
31630     isLayoutInstant : false, // needed?
31631     /**
31632      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31633      */   
31634     isResizingContainer : true,
31635     /**
31636      * @cfg {Number} columnWidth  width of the columns 
31637      */   
31638     
31639     columnWidth : 0,
31640     
31641     /**
31642      * @cfg {Number} maxCols maximum number of columns
31643      */   
31644     
31645     maxCols: 0,
31646     /**
31647      * @cfg {Number} padHeight padding below box..
31648      */   
31649     
31650     padHeight : 10, 
31651     
31652     /**
31653      * @cfg {Boolean} isAutoInitial defalut true
31654      */   
31655     
31656     isAutoInitial : true, 
31657     
31658     // private?
31659     gutter : 0,
31660     
31661     containerWidth: 0,
31662     initialColumnWidth : 0,
31663     currentSize : null,
31664     
31665     colYs : null, // array.
31666     maxY : 0,
31667     padWidth: 10,
31668     
31669     
31670     tag: 'div',
31671     cls: '',
31672     bricks: null, //CompositeElement
31673     cols : 0, // array?
31674     // element : null, // wrapped now this.el
31675     _isLayoutInited : null, 
31676     
31677     
31678     getAutoCreate : function(){
31679         
31680         var cfg = {
31681             tag: this.tag,
31682             cls: 'blog-masonary-wrapper ' + this.cls,
31683             cn : {
31684                 cls : 'mas-boxes masonary'
31685             }
31686         };
31687         
31688         return cfg;
31689     },
31690     
31691     getChildContainer: function( )
31692     {
31693         if (this.boxesEl) {
31694             return this.boxesEl;
31695         }
31696         
31697         this.boxesEl = this.el.select('.mas-boxes').first();
31698         
31699         return this.boxesEl;
31700     },
31701     
31702     
31703     initEvents : function()
31704     {
31705         var _this = this;
31706         
31707         if(this.isAutoInitial){
31708             Roo.log('hook children rendered');
31709             this.on('childrenrendered', function() {
31710                 Roo.log('children rendered');
31711                 _this.initial();
31712             } ,this);
31713         }
31714         
31715     },
31716     
31717     initial : function()
31718     {
31719         this.reloadItems();
31720
31721         this.currentSize = this.el.getBox(true);
31722
31723         /// was window resize... - let's see if this works..
31724         Roo.EventManager.onWindowResize(this.resize, this); 
31725
31726         if(!this.isAutoInitial){
31727             this.layout();
31728             return;
31729         }
31730         
31731         this.layout.defer(500,this);
31732     },
31733     
31734     reloadItems: function()
31735     {
31736         this.bricks = this.el.select('.masonry-brick', true);
31737         
31738         this.bricks.each(function(b) {
31739             //Roo.log(b.getSize());
31740             if (!b.attr('originalwidth')) {
31741                 b.attr('originalwidth',  b.getSize().width);
31742             }
31743             
31744         });
31745         
31746         Roo.log(this.bricks.elements.length);
31747     },
31748     
31749     resize : function()
31750     {
31751         Roo.log('resize');
31752         var cs = this.el.getBox(true);
31753         
31754         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31755             Roo.log("no change in with or X");
31756             return;
31757         }
31758         this.currentSize = cs;
31759         this.layout();
31760     },
31761     
31762     layout : function()
31763     {
31764          Roo.log('layout');
31765         this._resetLayout();
31766         //this._manageStamps();
31767       
31768         // don't animate first layout
31769         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31770         this.layoutItems( isInstant );
31771       
31772         // flag for initalized
31773         this._isLayoutInited = true;
31774     },
31775     
31776     layoutItems : function( isInstant )
31777     {
31778         //var items = this._getItemsForLayout( this.items );
31779         // original code supports filtering layout items.. we just ignore it..
31780         
31781         this._layoutItems( this.bricks , isInstant );
31782       
31783         this._postLayout();
31784     },
31785     _layoutItems : function ( items , isInstant)
31786     {
31787        //this.fireEvent( 'layout', this, items );
31788     
31789
31790         if ( !items || !items.elements.length ) {
31791           // no items, emit event with empty array
31792             return;
31793         }
31794
31795         var queue = [];
31796         items.each(function(item) {
31797             Roo.log("layout item");
31798             Roo.log(item);
31799             // get x/y object from method
31800             var position = this._getItemLayoutPosition( item );
31801             // enqueue
31802             position.item = item;
31803             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31804             queue.push( position );
31805         }, this);
31806       
31807         this._processLayoutQueue( queue );
31808     },
31809     /** Sets position of item in DOM
31810     * @param {Element} item
31811     * @param {Number} x - horizontal position
31812     * @param {Number} y - vertical position
31813     * @param {Boolean} isInstant - disables transitions
31814     */
31815     _processLayoutQueue : function( queue )
31816     {
31817         for ( var i=0, len = queue.length; i < len; i++ ) {
31818             var obj = queue[i];
31819             obj.item.position('absolute');
31820             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31821         }
31822     },
31823       
31824     
31825     /**
31826     * Any logic you want to do after each layout,
31827     * i.e. size the container
31828     */
31829     _postLayout : function()
31830     {
31831         this.resizeContainer();
31832     },
31833     
31834     resizeContainer : function()
31835     {
31836         if ( !this.isResizingContainer ) {
31837             return;
31838         }
31839         var size = this._getContainerSize();
31840         if ( size ) {
31841             this.el.setSize(size.width,size.height);
31842             this.boxesEl.setSize(size.width,size.height);
31843         }
31844     },
31845     
31846     
31847     
31848     _resetLayout : function()
31849     {
31850         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31851         this.colWidth = this.el.getWidth();
31852         //this.gutter = this.el.getWidth(); 
31853         
31854         this.measureColumns();
31855
31856         // reset column Y
31857         var i = this.cols;
31858         this.colYs = [];
31859         while (i--) {
31860             this.colYs.push( 0 );
31861         }
31862     
31863         this.maxY = 0;
31864     },
31865
31866     measureColumns : function()
31867     {
31868         this.getContainerWidth();
31869       // if columnWidth is 0, default to outerWidth of first item
31870         if ( !this.columnWidth ) {
31871             var firstItem = this.bricks.first();
31872             Roo.log(firstItem);
31873             this.columnWidth  = this.containerWidth;
31874             if (firstItem && firstItem.attr('originalwidth') ) {
31875                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31876             }
31877             // columnWidth fall back to item of first element
31878             Roo.log("set column width?");
31879                         this.initialColumnWidth = this.columnWidth  ;
31880
31881             // if first elem has no width, default to size of container
31882             
31883         }
31884         
31885         
31886         if (this.initialColumnWidth) {
31887             this.columnWidth = this.initialColumnWidth;
31888         }
31889         
31890         
31891             
31892         // column width is fixed at the top - however if container width get's smaller we should
31893         // reduce it...
31894         
31895         // this bit calcs how man columns..
31896             
31897         var columnWidth = this.columnWidth += this.gutter;
31898       
31899         // calculate columns
31900         var containerWidth = this.containerWidth + this.gutter;
31901         
31902         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31903         // fix rounding errors, typically with gutters
31904         var excess = columnWidth - containerWidth % columnWidth;
31905         
31906         
31907         // if overshoot is less than a pixel, round up, otherwise floor it
31908         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31909         cols = Math[ mathMethod ]( cols );
31910         this.cols = Math.max( cols, 1 );
31911         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31912         
31913          // padding positioning..
31914         var totalColWidth = this.cols * this.columnWidth;
31915         var padavail = this.containerWidth - totalColWidth;
31916         // so for 2 columns - we need 3 'pads'
31917         
31918         var padNeeded = (1+this.cols) * this.padWidth;
31919         
31920         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31921         
31922         this.columnWidth += padExtra
31923         //this.padWidth = Math.floor(padavail /  ( this.cols));
31924         
31925         // adjust colum width so that padding is fixed??
31926         
31927         // we have 3 columns ... total = width * 3
31928         // we have X left over... that should be used by 
31929         
31930         //if (this.expandC) {
31931             
31932         //}
31933         
31934         
31935         
31936     },
31937     
31938     getContainerWidth : function()
31939     {
31940        /* // container is parent if fit width
31941         var container = this.isFitWidth ? this.element.parentNode : this.element;
31942         // check that this.size and size are there
31943         // IE8 triggers resize on body size change, so they might not be
31944         
31945         var size = getSize( container );  //FIXME
31946         this.containerWidth = size && size.innerWidth; //FIXME
31947         */
31948          
31949         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31950         
31951     },
31952     
31953     _getItemLayoutPosition : function( item )  // what is item?
31954     {
31955         // we resize the item to our columnWidth..
31956       
31957         item.setWidth(this.columnWidth);
31958         item.autoBoxAdjust  = false;
31959         
31960         var sz = item.getSize();
31961  
31962         // how many columns does this brick span
31963         var remainder = this.containerWidth % this.columnWidth;
31964         
31965         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31966         // round if off by 1 pixel, otherwise use ceil
31967         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31968         colSpan = Math.min( colSpan, this.cols );
31969         
31970         // normally this should be '1' as we dont' currently allow multi width columns..
31971         
31972         var colGroup = this._getColGroup( colSpan );
31973         // get the minimum Y value from the columns
31974         var minimumY = Math.min.apply( Math, colGroup );
31975         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31976         
31977         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31978          
31979         // position the brick
31980         var position = {
31981             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31982             y: this.currentSize.y + minimumY + this.padHeight
31983         };
31984         
31985         Roo.log(position);
31986         // apply setHeight to necessary columns
31987         var setHeight = minimumY + sz.height + this.padHeight;
31988         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31989         
31990         var setSpan = this.cols + 1 - colGroup.length;
31991         for ( var i = 0; i < setSpan; i++ ) {
31992           this.colYs[ shortColIndex + i ] = setHeight ;
31993         }
31994       
31995         return position;
31996     },
31997     
31998     /**
31999      * @param {Number} colSpan - number of columns the element spans
32000      * @returns {Array} colGroup
32001      */
32002     _getColGroup : function( colSpan )
32003     {
32004         if ( colSpan < 2 ) {
32005           // if brick spans only one column, use all the column Ys
32006           return this.colYs;
32007         }
32008       
32009         var colGroup = [];
32010         // how many different places could this brick fit horizontally
32011         var groupCount = this.cols + 1 - colSpan;
32012         // for each group potential horizontal position
32013         for ( var i = 0; i < groupCount; i++ ) {
32014           // make an array of colY values for that one group
32015           var groupColYs = this.colYs.slice( i, i + colSpan );
32016           // and get the max value of the array
32017           colGroup[i] = Math.max.apply( Math, groupColYs );
32018         }
32019         return colGroup;
32020     },
32021     /*
32022     _manageStamp : function( stamp )
32023     {
32024         var stampSize =  stamp.getSize();
32025         var offset = stamp.getBox();
32026         // get the columns that this stamp affects
32027         var firstX = this.isOriginLeft ? offset.x : offset.right;
32028         var lastX = firstX + stampSize.width;
32029         var firstCol = Math.floor( firstX / this.columnWidth );
32030         firstCol = Math.max( 0, firstCol );
32031         
32032         var lastCol = Math.floor( lastX / this.columnWidth );
32033         // lastCol should not go over if multiple of columnWidth #425
32034         lastCol -= lastX % this.columnWidth ? 0 : 1;
32035         lastCol = Math.min( this.cols - 1, lastCol );
32036         
32037         // set colYs to bottom of the stamp
32038         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32039             stampSize.height;
32040             
32041         for ( var i = firstCol; i <= lastCol; i++ ) {
32042           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32043         }
32044     },
32045     */
32046     
32047     _getContainerSize : function()
32048     {
32049         this.maxY = Math.max.apply( Math, this.colYs );
32050         var size = {
32051             height: this.maxY
32052         };
32053       
32054         if ( this.isFitWidth ) {
32055             size.width = this._getContainerFitWidth();
32056         }
32057       
32058         return size;
32059     },
32060     
32061     _getContainerFitWidth : function()
32062     {
32063         var unusedCols = 0;
32064         // count unused columns
32065         var i = this.cols;
32066         while ( --i ) {
32067           if ( this.colYs[i] !== 0 ) {
32068             break;
32069           }
32070           unusedCols++;
32071         }
32072         // fit container to columns that have been used
32073         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32074     },
32075     
32076     needsResizeLayout : function()
32077     {
32078         var previousWidth = this.containerWidth;
32079         this.getContainerWidth();
32080         return previousWidth !== this.containerWidth;
32081     }
32082  
32083 });
32084
32085  
32086
32087  /*
32088  * - LGPL
32089  *
32090  * element
32091  * 
32092  */
32093
32094 /**
32095  * @class Roo.bootstrap.MasonryBrick
32096  * @extends Roo.bootstrap.Component
32097  * Bootstrap MasonryBrick class
32098  * 
32099  * @constructor
32100  * Create a new MasonryBrick
32101  * @param {Object} config The config object
32102  */
32103
32104 Roo.bootstrap.MasonryBrick = function(config){
32105     
32106     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32107     
32108     Roo.bootstrap.MasonryBrick.register(this);
32109     
32110     this.addEvents({
32111         // raw events
32112         /**
32113          * @event click
32114          * When a MasonryBrick is clcik
32115          * @param {Roo.bootstrap.MasonryBrick} this
32116          * @param {Roo.EventObject} e
32117          */
32118         "click" : true
32119     });
32120 };
32121
32122 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32123     
32124     /**
32125      * @cfg {String} title
32126      */   
32127     title : '',
32128     /**
32129      * @cfg {String} html
32130      */   
32131     html : '',
32132     /**
32133      * @cfg {String} bgimage
32134      */   
32135     bgimage : '',
32136     /**
32137      * @cfg {String} videourl
32138      */   
32139     videourl : '',
32140     /**
32141      * @cfg {String} cls
32142      */   
32143     cls : '',
32144     /**
32145      * @cfg {String} href
32146      */   
32147     href : '',
32148     /**
32149      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32150      */   
32151     size : 'xs',
32152     
32153     /**
32154      * @cfg {String} placetitle (center|bottom)
32155      */   
32156     placetitle : '',
32157     
32158     /**
32159      * @cfg {Boolean} isFitContainer defalut true
32160      */   
32161     isFitContainer : true, 
32162     
32163     /**
32164      * @cfg {Boolean} preventDefault defalut false
32165      */   
32166     preventDefault : false, 
32167     
32168     /**
32169      * @cfg {Boolean} inverse defalut false
32170      */   
32171     maskInverse : false, 
32172     
32173     getAutoCreate : function()
32174     {
32175         if(!this.isFitContainer){
32176             return this.getSplitAutoCreate();
32177         }
32178         
32179         var cls = 'masonry-brick masonry-brick-full';
32180         
32181         if(this.href.length){
32182             cls += ' masonry-brick-link';
32183         }
32184         
32185         if(this.bgimage.length){
32186             cls += ' masonry-brick-image';
32187         }
32188         
32189         if(this.maskInverse){
32190             cls += ' mask-inverse';
32191         }
32192         
32193         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32194             cls += ' enable-mask';
32195         }
32196         
32197         if(this.size){
32198             cls += ' masonry-' + this.size + '-brick';
32199         }
32200         
32201         if(this.placetitle.length){
32202             
32203             switch (this.placetitle) {
32204                 case 'center' :
32205                     cls += ' masonry-center-title';
32206                     break;
32207                 case 'bottom' :
32208                     cls += ' masonry-bottom-title';
32209                     break;
32210                 default:
32211                     break;
32212             }
32213             
32214         } else {
32215             if(!this.html.length && !this.bgimage.length){
32216                 cls += ' masonry-center-title';
32217             }
32218
32219             if(!this.html.length && this.bgimage.length){
32220                 cls += ' masonry-bottom-title';
32221             }
32222         }
32223         
32224         if(this.cls){
32225             cls += ' ' + this.cls;
32226         }
32227         
32228         var cfg = {
32229             tag: (this.href.length) ? 'a' : 'div',
32230             cls: cls,
32231             cn: [
32232                 {
32233                     tag: 'div',
32234                     cls: 'masonry-brick-mask'
32235                 },
32236                 {
32237                     tag: 'div',
32238                     cls: 'masonry-brick-paragraph',
32239                     cn: []
32240                 }
32241             ]
32242         };
32243         
32244         if(this.href.length){
32245             cfg.href = this.href;
32246         }
32247         
32248         var cn = cfg.cn[1].cn;
32249         
32250         if(this.title.length){
32251             cn.push({
32252                 tag: 'h4',
32253                 cls: 'masonry-brick-title',
32254                 html: this.title
32255             });
32256         }
32257         
32258         if(this.html.length){
32259             cn.push({
32260                 tag: 'p',
32261                 cls: 'masonry-brick-text',
32262                 html: this.html
32263             });
32264         }
32265         
32266         if (!this.title.length && !this.html.length) {
32267             cfg.cn[1].cls += ' hide';
32268         }
32269         
32270         if(this.bgimage.length){
32271             cfg.cn.push({
32272                 tag: 'img',
32273                 cls: 'masonry-brick-image-view',
32274                 src: this.bgimage
32275             });
32276         }
32277         
32278         if(this.videourl.length){
32279             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32280             // youtube support only?
32281             cfg.cn.push({
32282                 tag: 'iframe',
32283                 cls: 'masonry-brick-image-view',
32284                 src: vurl,
32285                 frameborder : 0,
32286                 allowfullscreen : true
32287             });
32288         }
32289         
32290         return cfg;
32291         
32292     },
32293     
32294     getSplitAutoCreate : function()
32295     {
32296         var cls = 'masonry-brick masonry-brick-split';
32297         
32298         if(this.href.length){
32299             cls += ' masonry-brick-link';
32300         }
32301         
32302         if(this.bgimage.length){
32303             cls += ' masonry-brick-image';
32304         }
32305         
32306         if(this.size){
32307             cls += ' masonry-' + this.size + '-brick';
32308         }
32309         
32310         switch (this.placetitle) {
32311             case 'center' :
32312                 cls += ' masonry-center-title';
32313                 break;
32314             case 'bottom' :
32315                 cls += ' masonry-bottom-title';
32316                 break;
32317             default:
32318                 if(!this.bgimage.length){
32319                     cls += ' masonry-center-title';
32320                 }
32321
32322                 if(this.bgimage.length){
32323                     cls += ' masonry-bottom-title';
32324                 }
32325                 break;
32326         }
32327         
32328         if(this.cls){
32329             cls += ' ' + this.cls;
32330         }
32331         
32332         var cfg = {
32333             tag: (this.href.length) ? 'a' : 'div',
32334             cls: cls,
32335             cn: [
32336                 {
32337                     tag: 'div',
32338                     cls: 'masonry-brick-split-head',
32339                     cn: [
32340                         {
32341                             tag: 'div',
32342                             cls: 'masonry-brick-paragraph',
32343                             cn: []
32344                         }
32345                     ]
32346                 },
32347                 {
32348                     tag: 'div',
32349                     cls: 'masonry-brick-split-body',
32350                     cn: []
32351                 }
32352             ]
32353         };
32354         
32355         if(this.href.length){
32356             cfg.href = this.href;
32357         }
32358         
32359         if(this.title.length){
32360             cfg.cn[0].cn[0].cn.push({
32361                 tag: 'h4',
32362                 cls: 'masonry-brick-title',
32363                 html: this.title
32364             });
32365         }
32366         
32367         if(this.html.length){
32368             cfg.cn[1].cn.push({
32369                 tag: 'p',
32370                 cls: 'masonry-brick-text',
32371                 html: this.html
32372             });
32373         }
32374
32375         if(this.bgimage.length){
32376             cfg.cn[0].cn.push({
32377                 tag: 'img',
32378                 cls: 'masonry-brick-image-view',
32379                 src: this.bgimage
32380             });
32381         }
32382         
32383         if(this.videourl.length){
32384             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32385             // youtube support only?
32386             cfg.cn[0].cn.cn.push({
32387                 tag: 'iframe',
32388                 cls: 'masonry-brick-image-view',
32389                 src: vurl,
32390                 frameborder : 0,
32391                 allowfullscreen : true
32392             });
32393         }
32394         
32395         return cfg;
32396     },
32397     
32398     initEvents: function() 
32399     {
32400         switch (this.size) {
32401             case 'xs' :
32402                 this.x = 1;
32403                 this.y = 1;
32404                 break;
32405             case 'sm' :
32406                 this.x = 2;
32407                 this.y = 2;
32408                 break;
32409             case 'md' :
32410             case 'md-left' :
32411             case 'md-right' :
32412                 this.x = 3;
32413                 this.y = 3;
32414                 break;
32415             case 'tall' :
32416                 this.x = 2;
32417                 this.y = 3;
32418                 break;
32419             case 'wide' :
32420                 this.x = 3;
32421                 this.y = 2;
32422                 break;
32423             case 'wide-thin' :
32424                 this.x = 3;
32425                 this.y = 1;
32426                 break;
32427                         
32428             default :
32429                 break;
32430         }
32431         
32432         if(Roo.isTouch){
32433             this.el.on('touchstart', this.onTouchStart, this);
32434             this.el.on('touchmove', this.onTouchMove, this);
32435             this.el.on('touchend', this.onTouchEnd, this);
32436             this.el.on('contextmenu', this.onContextMenu, this);
32437         } else {
32438             this.el.on('mouseenter'  ,this.enter, this);
32439             this.el.on('mouseleave', this.leave, this);
32440             this.el.on('click', this.onClick, this);
32441         }
32442         
32443         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32444             this.parent().bricks.push(this);   
32445         }
32446         
32447     },
32448     
32449     onClick: function(e, el)
32450     {
32451         var time = this.endTimer - this.startTimer;
32452         // Roo.log(e.preventDefault());
32453         if(Roo.isTouch){
32454             if(time > 1000){
32455                 e.preventDefault();
32456                 return;
32457             }
32458         }
32459         
32460         if(!this.preventDefault){
32461             return;
32462         }
32463         
32464         e.preventDefault();
32465         
32466         if (this.activcClass != '') {
32467             this.selectBrick();
32468         }
32469         
32470         this.fireEvent('click', this);
32471     },
32472     
32473     enter: function(e, el)
32474     {
32475         e.preventDefault();
32476         
32477         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32478             return;
32479         }
32480         
32481         if(this.bgimage.length && this.html.length){
32482             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32483         }
32484     },
32485     
32486     leave: function(e, el)
32487     {
32488         e.preventDefault();
32489         
32490         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32491             return;
32492         }
32493         
32494         if(this.bgimage.length && this.html.length){
32495             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32496         }
32497     },
32498     
32499     onTouchStart: function(e, el)
32500     {
32501 //        e.preventDefault();
32502         
32503         this.touchmoved = false;
32504         
32505         if(!this.isFitContainer){
32506             return;
32507         }
32508         
32509         if(!this.bgimage.length || !this.html.length){
32510             return;
32511         }
32512         
32513         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32514         
32515         this.timer = new Date().getTime();
32516         
32517     },
32518     
32519     onTouchMove: function(e, el)
32520     {
32521         this.touchmoved = true;
32522     },
32523     
32524     onContextMenu : function(e,el)
32525     {
32526         e.preventDefault();
32527         e.stopPropagation();
32528         return false;
32529     },
32530     
32531     onTouchEnd: function(e, el)
32532     {
32533 //        e.preventDefault();
32534         
32535         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32536         
32537             this.leave(e,el);
32538             
32539             return;
32540         }
32541         
32542         if(!this.bgimage.length || !this.html.length){
32543             
32544             if(this.href.length){
32545                 window.location.href = this.href;
32546             }
32547             
32548             return;
32549         }
32550         
32551         if(!this.isFitContainer){
32552             return;
32553         }
32554         
32555         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32556         
32557         window.location.href = this.href;
32558     },
32559     
32560     //selection on single brick only
32561     selectBrick : function() {
32562         
32563         if (!this.parentId) {
32564             return;
32565         }
32566         
32567         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32568         var index = m.selectedBrick.indexOf(this.id);
32569         
32570         if ( index > -1) {
32571             m.selectedBrick.splice(index,1);
32572             this.el.removeClass(this.activeClass);
32573             return;
32574         }
32575         
32576         for(var i = 0; i < m.selectedBrick.length; i++) {
32577             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32578             b.el.removeClass(b.activeClass);
32579         }
32580         
32581         m.selectedBrick = [];
32582         
32583         m.selectedBrick.push(this.id);
32584         this.el.addClass(this.activeClass);
32585         return;
32586     }
32587     
32588 });
32589
32590 Roo.apply(Roo.bootstrap.MasonryBrick, {
32591     
32592     //groups: {},
32593     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32594      /**
32595     * register a Masonry Brick
32596     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32597     */
32598     
32599     register : function(brick)
32600     {
32601         //this.groups[brick.id] = brick;
32602         this.groups.add(brick.id, brick);
32603     },
32604     /**
32605     * fetch a  masonry brick based on the masonry brick ID
32606     * @param {string} the masonry brick to add
32607     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32608     */
32609     
32610     get: function(brick_id) 
32611     {
32612         // if (typeof(this.groups[brick_id]) == 'undefined') {
32613         //     return false;
32614         // }
32615         // return this.groups[brick_id] ;
32616         
32617         if(this.groups.key(brick_id)) {
32618             return this.groups.key(brick_id);
32619         }
32620         
32621         return false;
32622     }
32623     
32624     
32625     
32626 });
32627
32628  /*
32629  * - LGPL
32630  *
32631  * element
32632  * 
32633  */
32634
32635 /**
32636  * @class Roo.bootstrap.Brick
32637  * @extends Roo.bootstrap.Component
32638  * Bootstrap Brick class
32639  * 
32640  * @constructor
32641  * Create a new Brick
32642  * @param {Object} config The config object
32643  */
32644
32645 Roo.bootstrap.Brick = function(config){
32646     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32647     
32648     this.addEvents({
32649         // raw events
32650         /**
32651          * @event click
32652          * When a Brick is click
32653          * @param {Roo.bootstrap.Brick} this
32654          * @param {Roo.EventObject} e
32655          */
32656         "click" : true
32657     });
32658 };
32659
32660 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32661     
32662     /**
32663      * @cfg {String} title
32664      */   
32665     title : '',
32666     /**
32667      * @cfg {String} html
32668      */   
32669     html : '',
32670     /**
32671      * @cfg {String} bgimage
32672      */   
32673     bgimage : '',
32674     /**
32675      * @cfg {String} cls
32676      */   
32677     cls : '',
32678     /**
32679      * @cfg {String} href
32680      */   
32681     href : '',
32682     /**
32683      * @cfg {String} video
32684      */   
32685     video : '',
32686     /**
32687      * @cfg {Boolean} square
32688      */   
32689     square : true,
32690     
32691     getAutoCreate : function()
32692     {
32693         var cls = 'roo-brick';
32694         
32695         if(this.href.length){
32696             cls += ' roo-brick-link';
32697         }
32698         
32699         if(this.bgimage.length){
32700             cls += ' roo-brick-image';
32701         }
32702         
32703         if(!this.html.length && !this.bgimage.length){
32704             cls += ' roo-brick-center-title';
32705         }
32706         
32707         if(!this.html.length && this.bgimage.length){
32708             cls += ' roo-brick-bottom-title';
32709         }
32710         
32711         if(this.cls){
32712             cls += ' ' + this.cls;
32713         }
32714         
32715         var cfg = {
32716             tag: (this.href.length) ? 'a' : 'div',
32717             cls: cls,
32718             cn: [
32719                 {
32720                     tag: 'div',
32721                     cls: 'roo-brick-paragraph',
32722                     cn: []
32723                 }
32724             ]
32725         };
32726         
32727         if(this.href.length){
32728             cfg.href = this.href;
32729         }
32730         
32731         var cn = cfg.cn[0].cn;
32732         
32733         if(this.title.length){
32734             cn.push({
32735                 tag: 'h4',
32736                 cls: 'roo-brick-title',
32737                 html: this.title
32738             });
32739         }
32740         
32741         if(this.html.length){
32742             cn.push({
32743                 tag: 'p',
32744                 cls: 'roo-brick-text',
32745                 html: this.html
32746             });
32747         } else {
32748             cn.cls += ' hide';
32749         }
32750         
32751         if(this.bgimage.length){
32752             cfg.cn.push({
32753                 tag: 'img',
32754                 cls: 'roo-brick-image-view',
32755                 src: this.bgimage
32756             });
32757         }
32758         
32759         return cfg;
32760     },
32761     
32762     initEvents: function() 
32763     {
32764         if(this.title.length || this.html.length){
32765             this.el.on('mouseenter'  ,this.enter, this);
32766             this.el.on('mouseleave', this.leave, this);
32767         }
32768         
32769         Roo.EventManager.onWindowResize(this.resize, this); 
32770         
32771         if(this.bgimage.length){
32772             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32773             this.imageEl.on('load', this.onImageLoad, this);
32774             return;
32775         }
32776         
32777         this.resize();
32778     },
32779     
32780     onImageLoad : function()
32781     {
32782         this.resize();
32783     },
32784     
32785     resize : function()
32786     {
32787         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32788         
32789         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32790         
32791         if(this.bgimage.length){
32792             var image = this.el.select('.roo-brick-image-view', true).first();
32793             
32794             image.setWidth(paragraph.getWidth());
32795             
32796             if(this.square){
32797                 image.setHeight(paragraph.getWidth());
32798             }
32799             
32800             this.el.setHeight(image.getHeight());
32801             paragraph.setHeight(image.getHeight());
32802             
32803         }
32804         
32805     },
32806     
32807     enter: function(e, el)
32808     {
32809         e.preventDefault();
32810         
32811         if(this.bgimage.length){
32812             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32813             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32814         }
32815     },
32816     
32817     leave: function(e, el)
32818     {
32819         e.preventDefault();
32820         
32821         if(this.bgimage.length){
32822             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32823             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32824         }
32825     }
32826     
32827 });
32828
32829  
32830
32831  /*
32832  * - LGPL
32833  *
32834  * Input
32835  * 
32836  */
32837
32838 /**
32839  * @class Roo.bootstrap.NumberField
32840  * @extends Roo.bootstrap.Input
32841  * Bootstrap NumberField class
32842  * 
32843  * 
32844  * 
32845  * 
32846  * @constructor
32847  * Create a new NumberField
32848  * @param {Object} config The config object
32849  */
32850
32851 Roo.bootstrap.NumberField = function(config){
32852     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32853 };
32854
32855 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32856     
32857     /**
32858      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32859      */
32860     allowDecimals : true,
32861     /**
32862      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32863      */
32864     decimalSeparator : ".",
32865     /**
32866      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32867      */
32868     decimalPrecision : 2,
32869     /**
32870      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32871      */
32872     allowNegative : true,
32873     /**
32874      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32875      */
32876     minValue : Number.NEGATIVE_INFINITY,
32877     /**
32878      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32879      */
32880     maxValue : Number.MAX_VALUE,
32881     /**
32882      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32883      */
32884     minText : "The minimum value for this field is {0}",
32885     /**
32886      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32887      */
32888     maxText : "The maximum value for this field is {0}",
32889     /**
32890      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32891      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32892      */
32893     nanText : "{0} is not a valid number",
32894     /**
32895      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32896      */
32897     castInt : true,
32898
32899     // private
32900     initEvents : function()
32901     {   
32902         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32903         
32904         var allowed = "0123456789";
32905         
32906         if(this.allowDecimals){
32907             allowed += this.decimalSeparator;
32908         }
32909         
32910         if(this.allowNegative){
32911             allowed += "-";
32912         }
32913         
32914         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32915         
32916         var keyPress = function(e){
32917             
32918             var k = e.getKey();
32919             
32920             var c = e.getCharCode();
32921             
32922             if(
32923                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32924                     allowed.indexOf(String.fromCharCode(c)) === -1
32925             ){
32926                 e.stopEvent();
32927                 return;
32928             }
32929             
32930             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32931                 return;
32932             }
32933             
32934             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32935                 e.stopEvent();
32936             }
32937         };
32938         
32939         this.el.on("keypress", keyPress, this);
32940     },
32941     
32942     validateValue : function(value)
32943     {
32944         
32945         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32946             return false;
32947         }
32948         
32949         var num = this.parseValue(value);
32950         
32951         if(isNaN(num)){
32952             this.markInvalid(String.format(this.nanText, value));
32953             return false;
32954         }
32955         
32956         if(num < this.minValue){
32957             this.markInvalid(String.format(this.minText, this.minValue));
32958             return false;
32959         }
32960         
32961         if(num > this.maxValue){
32962             this.markInvalid(String.format(this.maxText, this.maxValue));
32963             return false;
32964         }
32965         
32966         return true;
32967     },
32968
32969     getValue : function()
32970     {
32971         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32972     },
32973
32974     parseValue : function(value)
32975     {
32976         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32977         return isNaN(value) ? '' : value;
32978     },
32979
32980     fixPrecision : function(value)
32981     {
32982         var nan = isNaN(value);
32983         
32984         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32985             return nan ? '' : value;
32986         }
32987         return parseFloat(value).toFixed(this.decimalPrecision);
32988     },
32989
32990     setValue : function(v)
32991     {
32992         v = this.fixPrecision(v);
32993         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32994     },
32995
32996     decimalPrecisionFcn : function(v)
32997     {
32998         return Math.floor(v);
32999     },
33000
33001     beforeBlur : function()
33002     {
33003         if(!this.castInt){
33004             return;
33005         }
33006         
33007         var v = this.parseValue(this.getRawValue());
33008         if(v){
33009             this.setValue(v);
33010         }
33011     }
33012     
33013 });
33014
33015  
33016
33017 /*
33018 * Licence: LGPL
33019 */
33020
33021 /**
33022  * @class Roo.bootstrap.DocumentSlider
33023  * @extends Roo.bootstrap.Component
33024  * Bootstrap DocumentSlider class
33025  * 
33026  * @constructor
33027  * Create a new DocumentViewer
33028  * @param {Object} config The config object
33029  */
33030
33031 Roo.bootstrap.DocumentSlider = function(config){
33032     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33033     
33034     this.files = [];
33035     
33036     this.addEvents({
33037         /**
33038          * @event initial
33039          * Fire after initEvent
33040          * @param {Roo.bootstrap.DocumentSlider} this
33041          */
33042         "initial" : true,
33043         /**
33044          * @event update
33045          * Fire after update
33046          * @param {Roo.bootstrap.DocumentSlider} this
33047          */
33048         "update" : true,
33049         /**
33050          * @event click
33051          * Fire after click
33052          * @param {Roo.bootstrap.DocumentSlider} this
33053          */
33054         "click" : true
33055     });
33056 };
33057
33058 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33059     
33060     files : false,
33061     
33062     indicator : 0,
33063     
33064     getAutoCreate : function()
33065     {
33066         var cfg = {
33067             tag : 'div',
33068             cls : 'roo-document-slider',
33069             cn : [
33070                 {
33071                     tag : 'div',
33072                     cls : 'roo-document-slider-header',
33073                     cn : [
33074                         {
33075                             tag : 'div',
33076                             cls : 'roo-document-slider-header-title'
33077                         }
33078                     ]
33079                 },
33080                 {
33081                     tag : 'div',
33082                     cls : 'roo-document-slider-body',
33083                     cn : [
33084                         {
33085                             tag : 'div',
33086                             cls : 'roo-document-slider-prev',
33087                             cn : [
33088                                 {
33089                                     tag : 'i',
33090                                     cls : 'fa fa-chevron-left'
33091                                 }
33092                             ]
33093                         },
33094                         {
33095                             tag : 'div',
33096                             cls : 'roo-document-slider-thumb',
33097                             cn : [
33098                                 {
33099                                     tag : 'img',
33100                                     cls : 'roo-document-slider-image'
33101                                 }
33102                             ]
33103                         },
33104                         {
33105                             tag : 'div',
33106                             cls : 'roo-document-slider-next',
33107                             cn : [
33108                                 {
33109                                     tag : 'i',
33110                                     cls : 'fa fa-chevron-right'
33111                                 }
33112                             ]
33113                         }
33114                     ]
33115                 }
33116             ]
33117         };
33118         
33119         return cfg;
33120     },
33121     
33122     initEvents : function()
33123     {
33124         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33125         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33126         
33127         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33128         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33129         
33130         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33131         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33132         
33133         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33134         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33135         
33136         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33137         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33138         
33139         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33140         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33141         
33142         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33143         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33144         
33145         this.thumbEl.on('click', this.onClick, this);
33146         
33147         this.prevIndicator.on('click', this.prev, this);
33148         
33149         this.nextIndicator.on('click', this.next, this);
33150         
33151     },
33152     
33153     initial : function()
33154     {
33155         if(this.files.length){
33156             this.indicator = 1;
33157             this.update()
33158         }
33159         
33160         this.fireEvent('initial', this);
33161     },
33162     
33163     update : function()
33164     {
33165         this.imageEl.attr('src', this.files[this.indicator - 1]);
33166         
33167         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33168         
33169         this.prevIndicator.show();
33170         
33171         if(this.indicator == 1){
33172             this.prevIndicator.hide();
33173         }
33174         
33175         this.nextIndicator.show();
33176         
33177         if(this.indicator == this.files.length){
33178             this.nextIndicator.hide();
33179         }
33180         
33181         this.thumbEl.scrollTo('top');
33182         
33183         this.fireEvent('update', this);
33184     },
33185     
33186     onClick : function(e)
33187     {
33188         e.preventDefault();
33189         
33190         this.fireEvent('click', this);
33191     },
33192     
33193     prev : function(e)
33194     {
33195         e.preventDefault();
33196         
33197         this.indicator = Math.max(1, this.indicator - 1);
33198         
33199         this.update();
33200     },
33201     
33202     next : function(e)
33203     {
33204         e.preventDefault();
33205         
33206         this.indicator = Math.min(this.files.length, this.indicator + 1);
33207         
33208         this.update();
33209     }
33210 });
33211 /*
33212  * - LGPL
33213  *
33214  * RadioSet
33215  *
33216  *
33217  */
33218
33219 /**
33220  * @class Roo.bootstrap.RadioSet
33221  * @extends Roo.bootstrap.Input
33222  * Bootstrap RadioSet class
33223  * @cfg {String} indicatorpos (left|right) default left
33224  * @cfg {Boolean} inline (true|false) inline the element (default true)
33225  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33226  * @constructor
33227  * Create a new RadioSet
33228  * @param {Object} config The config object
33229  */
33230
33231 Roo.bootstrap.RadioSet = function(config){
33232     
33233     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33234     
33235     this.radioes = [];
33236     
33237     Roo.bootstrap.RadioSet.register(this);
33238     
33239     this.addEvents({
33240         /**
33241         * @event check
33242         * Fires when the element is checked or unchecked.
33243         * @param {Roo.bootstrap.RadioSet} this This radio
33244         * @param {Roo.bootstrap.Radio} item The checked item
33245         */
33246        check : true
33247     });
33248     
33249 };
33250
33251 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33252
33253     radioes : false,
33254     
33255     inline : true,
33256     
33257     weight : '',
33258     
33259     indicatorpos : 'left',
33260     
33261     getAutoCreate : function()
33262     {
33263         var label = {
33264             tag : 'label',
33265             cls : 'roo-radio-set-label',
33266             cn : [
33267                 {
33268                     tag : 'span',
33269                     html : this.fieldLabel
33270                 }
33271             ]
33272         };
33273         
33274         if(this.indicatorpos == 'left'){
33275             label.cn.unshift({
33276                 tag : 'i',
33277                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33278                 tooltip : 'This field is required'
33279             });
33280         } else {
33281             label.cn.push({
33282                 tag : 'i',
33283                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33284                 tooltip : 'This field is required'
33285             });
33286         }
33287         
33288         var items = {
33289             tag : 'div',
33290             cls : 'roo-radio-set-items'
33291         };
33292         
33293         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33294         
33295         if (align === 'left' && this.fieldLabel.length) {
33296             
33297             items = {
33298                 cls : "roo-radio-set-right", 
33299                 cn: [
33300                     items
33301                 ]
33302             };
33303             
33304             if(this.labelWidth > 12){
33305                 label.style = "width: " + this.labelWidth + 'px';
33306             }
33307             
33308             if(this.labelWidth < 13 && this.labelmd == 0){
33309                 this.labelmd = this.labelWidth;
33310             }
33311             
33312             if(this.labellg > 0){
33313                 label.cls += ' col-lg-' + this.labellg;
33314                 items.cls += ' col-lg-' + (12 - this.labellg);
33315             }
33316             
33317             if(this.labelmd > 0){
33318                 label.cls += ' col-md-' + this.labelmd;
33319                 items.cls += ' col-md-' + (12 - this.labelmd);
33320             }
33321             
33322             if(this.labelsm > 0){
33323                 label.cls += ' col-sm-' + this.labelsm;
33324                 items.cls += ' col-sm-' + (12 - this.labelsm);
33325             }
33326             
33327             if(this.labelxs > 0){
33328                 label.cls += ' col-xs-' + this.labelxs;
33329                 items.cls += ' col-xs-' + (12 - this.labelxs);
33330             }
33331         }
33332         
33333         var cfg = {
33334             tag : 'div',
33335             cls : 'roo-radio-set',
33336             cn : [
33337                 {
33338                     tag : 'input',
33339                     cls : 'roo-radio-set-input',
33340                     type : 'hidden',
33341                     name : this.name,
33342                     value : this.value ? this.value :  ''
33343                 },
33344                 label,
33345                 items
33346             ]
33347         };
33348         
33349         if(this.weight.length){
33350             cfg.cls += ' roo-radio-' + this.weight;
33351         }
33352         
33353         if(this.inline) {
33354             cfg.cls += ' roo-radio-set-inline';
33355         }
33356         
33357         var settings=this;
33358         ['xs','sm','md','lg'].map(function(size){
33359             if (settings[size]) {
33360                 cfg.cls += ' col-' + size + '-' + settings[size];
33361             }
33362         });
33363         
33364         return cfg;
33365         
33366     },
33367
33368     initEvents : function()
33369     {
33370         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33371         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33372         
33373         if(!this.fieldLabel.length){
33374             this.labelEl.hide();
33375         }
33376         
33377         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33378         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33379         
33380         this.indicatorEl().addClass('invisible');
33381         
33382         this.originalValue = this.getValue();
33383         
33384     },
33385     
33386     inputEl: function ()
33387     {
33388         return this.el.select('.roo-radio-set-input', true).first();
33389     },
33390     
33391     getChildContainer : function()
33392     {
33393         return this.itemsEl;
33394     },
33395     
33396     register : function(item)
33397     {
33398         this.radioes.push(item);
33399         
33400     },
33401     
33402     validate : function()
33403     {   
33404         var valid = false;
33405         
33406         Roo.each(this.radioes, function(i){
33407             if(!i.checked){
33408                 return;
33409             }
33410             
33411             valid = true;
33412             return false;
33413         });
33414         
33415         if(this.allowBlank) {
33416             return true;
33417         }
33418         
33419         if(this.disabled || valid){
33420             this.markValid();
33421             return true;
33422         }
33423         
33424         this.markInvalid();
33425         return false;
33426         
33427     },
33428     
33429     markValid : function()
33430     {
33431         if(this.labelEl.isVisible(true)){
33432             this.indicatorEl().removeClass('visible');
33433             this.indicatorEl().addClass('invisible');
33434         }
33435         
33436         this.el.removeClass([this.invalidClass, this.validClass]);
33437         this.el.addClass(this.validClass);
33438         
33439         this.fireEvent('valid', this);
33440     },
33441     
33442     markInvalid : function(msg)
33443     {
33444         if(this.allowBlank || this.disabled){
33445             return;
33446         }
33447         
33448         if(this.labelEl.isVisible(true)){
33449             this.indicatorEl().removeClass('invisible');
33450             this.indicatorEl().addClass('visible');
33451         }
33452         
33453         this.el.removeClass([this.invalidClass, this.validClass]);
33454         this.el.addClass(this.invalidClass);
33455         
33456         this.fireEvent('invalid', this, msg);
33457         
33458     },
33459     
33460     setValue : function(v, suppressEvent)
33461     {   
33462         this.value = v;
33463         if(this.rendered){
33464             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33465         }
33466         
33467         Roo.each(this.radioes, function(i){
33468             
33469             i.checked = false;
33470             i.el.removeClass('checked');
33471             
33472             if(i.value === v || i.value.toString() === v.toString()){
33473                 i.checked = true;
33474                 i.el.addClass('checked');
33475                 
33476                 if(suppressEvent !== true){
33477                     this.fireEvent('check', this, i);
33478                 }
33479             }
33480             
33481         }, this);
33482         
33483         this.validate();
33484     },
33485     
33486     clearInvalid : function(){
33487         
33488         if(!this.el || this.preventMark){
33489             return;
33490         }
33491         
33492         this.el.removeClass([this.invalidClass]);
33493         
33494         this.fireEvent('valid', this);
33495     }
33496     
33497 });
33498
33499 Roo.apply(Roo.bootstrap.RadioSet, {
33500     
33501     groups: {},
33502     
33503     register : function(set)
33504     {
33505         this.groups[set.name] = set;
33506     },
33507     
33508     get: function(name) 
33509     {
33510         if (typeof(this.groups[name]) == 'undefined') {
33511             return false;
33512         }
33513         
33514         return this.groups[name] ;
33515     }
33516     
33517 });
33518 /*
33519  * Based on:
33520  * Ext JS Library 1.1.1
33521  * Copyright(c) 2006-2007, Ext JS, LLC.
33522  *
33523  * Originally Released Under LGPL - original licence link has changed is not relivant.
33524  *
33525  * Fork - LGPL
33526  * <script type="text/javascript">
33527  */
33528
33529
33530 /**
33531  * @class Roo.bootstrap.SplitBar
33532  * @extends Roo.util.Observable
33533  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33534  * <br><br>
33535  * Usage:
33536  * <pre><code>
33537 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33538                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33539 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33540 split.minSize = 100;
33541 split.maxSize = 600;
33542 split.animate = true;
33543 split.on('moved', splitterMoved);
33544 </code></pre>
33545  * @constructor
33546  * Create a new SplitBar
33547  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33548  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33549  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33550  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33551                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33552                         position of the SplitBar).
33553  */
33554 Roo.bootstrap.SplitBar = function(cfg){
33555     
33556     /** @private */
33557     
33558     //{
33559     //  dragElement : elm
33560     //  resizingElement: el,
33561         // optional..
33562     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33563     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33564         // existingProxy ???
33565     //}
33566     
33567     this.el = Roo.get(cfg.dragElement, true);
33568     this.el.dom.unselectable = "on";
33569     /** @private */
33570     this.resizingEl = Roo.get(cfg.resizingElement, true);
33571
33572     /**
33573      * @private
33574      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33575      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33576      * @type Number
33577      */
33578     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33579     
33580     /**
33581      * The minimum size of the resizing element. (Defaults to 0)
33582      * @type Number
33583      */
33584     this.minSize = 0;
33585     
33586     /**
33587      * The maximum size of the resizing element. (Defaults to 2000)
33588      * @type Number
33589      */
33590     this.maxSize = 2000;
33591     
33592     /**
33593      * Whether to animate the transition to the new size
33594      * @type Boolean
33595      */
33596     this.animate = false;
33597     
33598     /**
33599      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33600      * @type Boolean
33601      */
33602     this.useShim = false;
33603     
33604     /** @private */
33605     this.shim = null;
33606     
33607     if(!cfg.existingProxy){
33608         /** @private */
33609         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33610     }else{
33611         this.proxy = Roo.get(cfg.existingProxy).dom;
33612     }
33613     /** @private */
33614     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33615     
33616     /** @private */
33617     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33618     
33619     /** @private */
33620     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33621     
33622     /** @private */
33623     this.dragSpecs = {};
33624     
33625     /**
33626      * @private The adapter to use to positon and resize elements
33627      */
33628     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33629     this.adapter.init(this);
33630     
33631     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33632         /** @private */
33633         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33634         this.el.addClass("roo-splitbar-h");
33635     }else{
33636         /** @private */
33637         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33638         this.el.addClass("roo-splitbar-v");
33639     }
33640     
33641     this.addEvents({
33642         /**
33643          * @event resize
33644          * Fires when the splitter is moved (alias for {@link #event-moved})
33645          * @param {Roo.bootstrap.SplitBar} this
33646          * @param {Number} newSize the new width or height
33647          */
33648         "resize" : true,
33649         /**
33650          * @event moved
33651          * Fires when the splitter is moved
33652          * @param {Roo.bootstrap.SplitBar} this
33653          * @param {Number} newSize the new width or height
33654          */
33655         "moved" : true,
33656         /**
33657          * @event beforeresize
33658          * Fires before the splitter is dragged
33659          * @param {Roo.bootstrap.SplitBar} this
33660          */
33661         "beforeresize" : true,
33662
33663         "beforeapply" : true
33664     });
33665
33666     Roo.util.Observable.call(this);
33667 };
33668
33669 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33670     onStartProxyDrag : function(x, y){
33671         this.fireEvent("beforeresize", this);
33672         if(!this.overlay){
33673             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33674             o.unselectable();
33675             o.enableDisplayMode("block");
33676             // all splitbars share the same overlay
33677             Roo.bootstrap.SplitBar.prototype.overlay = o;
33678         }
33679         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33680         this.overlay.show();
33681         Roo.get(this.proxy).setDisplayed("block");
33682         var size = this.adapter.getElementSize(this);
33683         this.activeMinSize = this.getMinimumSize();;
33684         this.activeMaxSize = this.getMaximumSize();;
33685         var c1 = size - this.activeMinSize;
33686         var c2 = Math.max(this.activeMaxSize - size, 0);
33687         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33688             this.dd.resetConstraints();
33689             this.dd.setXConstraint(
33690                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33691                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33692             );
33693             this.dd.setYConstraint(0, 0);
33694         }else{
33695             this.dd.resetConstraints();
33696             this.dd.setXConstraint(0, 0);
33697             this.dd.setYConstraint(
33698                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33699                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33700             );
33701          }
33702         this.dragSpecs.startSize = size;
33703         this.dragSpecs.startPoint = [x, y];
33704         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33705     },
33706     
33707     /** 
33708      * @private Called after the drag operation by the DDProxy
33709      */
33710     onEndProxyDrag : function(e){
33711         Roo.get(this.proxy).setDisplayed(false);
33712         var endPoint = Roo.lib.Event.getXY(e);
33713         if(this.overlay){
33714             this.overlay.hide();
33715         }
33716         var newSize;
33717         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33718             newSize = this.dragSpecs.startSize + 
33719                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33720                     endPoint[0] - this.dragSpecs.startPoint[0] :
33721                     this.dragSpecs.startPoint[0] - endPoint[0]
33722                 );
33723         }else{
33724             newSize = this.dragSpecs.startSize + 
33725                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33726                     endPoint[1] - this.dragSpecs.startPoint[1] :
33727                     this.dragSpecs.startPoint[1] - endPoint[1]
33728                 );
33729         }
33730         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33731         if(newSize != this.dragSpecs.startSize){
33732             if(this.fireEvent('beforeapply', this, newSize) !== false){
33733                 this.adapter.setElementSize(this, newSize);
33734                 this.fireEvent("moved", this, newSize);
33735                 this.fireEvent("resize", this, newSize);
33736             }
33737         }
33738     },
33739     
33740     /**
33741      * Get the adapter this SplitBar uses
33742      * @return The adapter object
33743      */
33744     getAdapter : function(){
33745         return this.adapter;
33746     },
33747     
33748     /**
33749      * Set the adapter this SplitBar uses
33750      * @param {Object} adapter A SplitBar adapter object
33751      */
33752     setAdapter : function(adapter){
33753         this.adapter = adapter;
33754         this.adapter.init(this);
33755     },
33756     
33757     /**
33758      * Gets the minimum size for the resizing element
33759      * @return {Number} The minimum size
33760      */
33761     getMinimumSize : function(){
33762         return this.minSize;
33763     },
33764     
33765     /**
33766      * Sets the minimum size for the resizing element
33767      * @param {Number} minSize The minimum size
33768      */
33769     setMinimumSize : function(minSize){
33770         this.minSize = minSize;
33771     },
33772     
33773     /**
33774      * Gets the maximum size for the resizing element
33775      * @return {Number} The maximum size
33776      */
33777     getMaximumSize : function(){
33778         return this.maxSize;
33779     },
33780     
33781     /**
33782      * Sets the maximum size for the resizing element
33783      * @param {Number} maxSize The maximum size
33784      */
33785     setMaximumSize : function(maxSize){
33786         this.maxSize = maxSize;
33787     },
33788     
33789     /**
33790      * Sets the initialize size for the resizing element
33791      * @param {Number} size The initial size
33792      */
33793     setCurrentSize : function(size){
33794         var oldAnimate = this.animate;
33795         this.animate = false;
33796         this.adapter.setElementSize(this, size);
33797         this.animate = oldAnimate;
33798     },
33799     
33800     /**
33801      * Destroy this splitbar. 
33802      * @param {Boolean} removeEl True to remove the element
33803      */
33804     destroy : function(removeEl){
33805         if(this.shim){
33806             this.shim.remove();
33807         }
33808         this.dd.unreg();
33809         this.proxy.parentNode.removeChild(this.proxy);
33810         if(removeEl){
33811             this.el.remove();
33812         }
33813     }
33814 });
33815
33816 /**
33817  * @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.
33818  */
33819 Roo.bootstrap.SplitBar.createProxy = function(dir){
33820     var proxy = new Roo.Element(document.createElement("div"));
33821     proxy.unselectable();
33822     var cls = 'roo-splitbar-proxy';
33823     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33824     document.body.appendChild(proxy.dom);
33825     return proxy.dom;
33826 };
33827
33828 /** 
33829  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33830  * Default Adapter. It assumes the splitter and resizing element are not positioned
33831  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33832  */
33833 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33834 };
33835
33836 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33837     // do nothing for now
33838     init : function(s){
33839     
33840     },
33841     /**
33842      * Called before drag operations to get the current size of the resizing element. 
33843      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33844      */
33845      getElementSize : function(s){
33846         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33847             return s.resizingEl.getWidth();
33848         }else{
33849             return s.resizingEl.getHeight();
33850         }
33851     },
33852     
33853     /**
33854      * Called after drag operations to set the size of the resizing element.
33855      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33856      * @param {Number} newSize The new size to set
33857      * @param {Function} onComplete A function to be invoked when resizing is complete
33858      */
33859     setElementSize : function(s, newSize, onComplete){
33860         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33861             if(!s.animate){
33862                 s.resizingEl.setWidth(newSize);
33863                 if(onComplete){
33864                     onComplete(s, newSize);
33865                 }
33866             }else{
33867                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33868             }
33869         }else{
33870             
33871             if(!s.animate){
33872                 s.resizingEl.setHeight(newSize);
33873                 if(onComplete){
33874                     onComplete(s, newSize);
33875                 }
33876             }else{
33877                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33878             }
33879         }
33880     }
33881 };
33882
33883 /** 
33884  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33885  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33886  * Adapter that  moves the splitter element to align with the resized sizing element. 
33887  * Used with an absolute positioned SplitBar.
33888  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33889  * document.body, make sure you assign an id to the body element.
33890  */
33891 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33892     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33893     this.container = Roo.get(container);
33894 };
33895
33896 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33897     init : function(s){
33898         this.basic.init(s);
33899     },
33900     
33901     getElementSize : function(s){
33902         return this.basic.getElementSize(s);
33903     },
33904     
33905     setElementSize : function(s, newSize, onComplete){
33906         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33907     },
33908     
33909     moveSplitter : function(s){
33910         var yes = Roo.bootstrap.SplitBar;
33911         switch(s.placement){
33912             case yes.LEFT:
33913                 s.el.setX(s.resizingEl.getRight());
33914                 break;
33915             case yes.RIGHT:
33916                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33917                 break;
33918             case yes.TOP:
33919                 s.el.setY(s.resizingEl.getBottom());
33920                 break;
33921             case yes.BOTTOM:
33922                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33923                 break;
33924         }
33925     }
33926 };
33927
33928 /**
33929  * Orientation constant - Create a vertical SplitBar
33930  * @static
33931  * @type Number
33932  */
33933 Roo.bootstrap.SplitBar.VERTICAL = 1;
33934
33935 /**
33936  * Orientation constant - Create a horizontal SplitBar
33937  * @static
33938  * @type Number
33939  */
33940 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33941
33942 /**
33943  * Placement constant - The resizing element is to the left of the splitter element
33944  * @static
33945  * @type Number
33946  */
33947 Roo.bootstrap.SplitBar.LEFT = 1;
33948
33949 /**
33950  * Placement constant - The resizing element is to the right of the splitter element
33951  * @static
33952  * @type Number
33953  */
33954 Roo.bootstrap.SplitBar.RIGHT = 2;
33955
33956 /**
33957  * Placement constant - The resizing element is positioned above the splitter element
33958  * @static
33959  * @type Number
33960  */
33961 Roo.bootstrap.SplitBar.TOP = 3;
33962
33963 /**
33964  * Placement constant - The resizing element is positioned under splitter element
33965  * @static
33966  * @type Number
33967  */
33968 Roo.bootstrap.SplitBar.BOTTOM = 4;
33969 Roo.namespace("Roo.bootstrap.layout");/*
33970  * Based on:
33971  * Ext JS Library 1.1.1
33972  * Copyright(c) 2006-2007, Ext JS, LLC.
33973  *
33974  * Originally Released Under LGPL - original licence link has changed is not relivant.
33975  *
33976  * Fork - LGPL
33977  * <script type="text/javascript">
33978  */
33979
33980 /**
33981  * @class Roo.bootstrap.layout.Manager
33982  * @extends Roo.bootstrap.Component
33983  * Base class for layout managers.
33984  */
33985 Roo.bootstrap.layout.Manager = function(config)
33986 {
33987     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33988
33989
33990
33991
33992
33993     /** false to disable window resize monitoring @type Boolean */
33994     this.monitorWindowResize = true;
33995     this.regions = {};
33996     this.addEvents({
33997         /**
33998          * @event layout
33999          * Fires when a layout is performed.
34000          * @param {Roo.LayoutManager} this
34001          */
34002         "layout" : true,
34003         /**
34004          * @event regionresized
34005          * Fires when the user resizes a region.
34006          * @param {Roo.LayoutRegion} region The resized region
34007          * @param {Number} newSize The new size (width for east/west, height for north/south)
34008          */
34009         "regionresized" : true,
34010         /**
34011          * @event regioncollapsed
34012          * Fires when a region is collapsed.
34013          * @param {Roo.LayoutRegion} region The collapsed region
34014          */
34015         "regioncollapsed" : true,
34016         /**
34017          * @event regionexpanded
34018          * Fires when a region is expanded.
34019          * @param {Roo.LayoutRegion} region The expanded region
34020          */
34021         "regionexpanded" : true
34022     });
34023     this.updating = false;
34024
34025     if (config.el) {
34026         this.el = Roo.get(config.el);
34027         this.initEvents();
34028     }
34029
34030 };
34031
34032 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34033
34034
34035     regions : null,
34036
34037     monitorWindowResize : true,
34038
34039
34040     updating : false,
34041
34042
34043     onRender : function(ct, position)
34044     {
34045         if(!this.el){
34046             this.el = Roo.get(ct);
34047             this.initEvents();
34048         }
34049         //this.fireEvent('render',this);
34050     },
34051
34052
34053     initEvents: function()
34054     {
34055
34056
34057         // ie scrollbar fix
34058         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34059             document.body.scroll = "no";
34060         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34061             this.el.position('relative');
34062         }
34063         this.id = this.el.id;
34064         this.el.addClass("roo-layout-container");
34065         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34066         if(this.el.dom != document.body ) {
34067             this.el.on('resize', this.layout,this);
34068             this.el.on('show', this.layout,this);
34069         }
34070
34071     },
34072
34073     /**
34074      * Returns true if this layout is currently being updated
34075      * @return {Boolean}
34076      */
34077     isUpdating : function(){
34078         return this.updating;
34079     },
34080
34081     /**
34082      * Suspend the LayoutManager from doing auto-layouts while
34083      * making multiple add or remove calls
34084      */
34085     beginUpdate : function(){
34086         this.updating = true;
34087     },
34088
34089     /**
34090      * Restore auto-layouts and optionally disable the manager from performing a layout
34091      * @param {Boolean} noLayout true to disable a layout update
34092      */
34093     endUpdate : function(noLayout){
34094         this.updating = false;
34095         if(!noLayout){
34096             this.layout();
34097         }
34098     },
34099
34100     layout: function(){
34101         // abstract...
34102     },
34103
34104     onRegionResized : function(region, newSize){
34105         this.fireEvent("regionresized", region, newSize);
34106         this.layout();
34107     },
34108
34109     onRegionCollapsed : function(region){
34110         this.fireEvent("regioncollapsed", region);
34111     },
34112
34113     onRegionExpanded : function(region){
34114         this.fireEvent("regionexpanded", region);
34115     },
34116
34117     /**
34118      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34119      * performs box-model adjustments.
34120      * @return {Object} The size as an object {width: (the width), height: (the height)}
34121      */
34122     getViewSize : function()
34123     {
34124         var size;
34125         if(this.el.dom != document.body){
34126             size = this.el.getSize();
34127         }else{
34128             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34129         }
34130         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34131         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34132         return size;
34133     },
34134
34135     /**
34136      * Returns the Element this layout is bound to.
34137      * @return {Roo.Element}
34138      */
34139     getEl : function(){
34140         return this.el;
34141     },
34142
34143     /**
34144      * Returns the specified region.
34145      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34146      * @return {Roo.LayoutRegion}
34147      */
34148     getRegion : function(target){
34149         return this.regions[target.toLowerCase()];
34150     },
34151
34152     onWindowResize : function(){
34153         if(this.monitorWindowResize){
34154             this.layout();
34155         }
34156     }
34157 });
34158 /*
34159  * Based on:
34160  * Ext JS Library 1.1.1
34161  * Copyright(c) 2006-2007, Ext JS, LLC.
34162  *
34163  * Originally Released Under LGPL - original licence link has changed is not relivant.
34164  *
34165  * Fork - LGPL
34166  * <script type="text/javascript">
34167  */
34168 /**
34169  * @class Roo.bootstrap.layout.Border
34170  * @extends Roo.bootstrap.layout.Manager
34171  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34172  * please see: examples/bootstrap/nested.html<br><br>
34173  
34174 <b>The container the layout is rendered into can be either the body element or any other element.
34175 If it is not the body element, the container needs to either be an absolute positioned element,
34176 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34177 the container size if it is not the body element.</b>
34178
34179 * @constructor
34180 * Create a new Border
34181 * @param {Object} config Configuration options
34182  */
34183 Roo.bootstrap.layout.Border = function(config){
34184     config = config || {};
34185     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34186     
34187     
34188     
34189     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34190         if(config[region]){
34191             config[region].region = region;
34192             this.addRegion(config[region]);
34193         }
34194     },this);
34195     
34196 };
34197
34198 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34199
34200 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34201     /**
34202      * Creates and adds a new region if it doesn't already exist.
34203      * @param {String} target The target region key (north, south, east, west or center).
34204      * @param {Object} config The regions config object
34205      * @return {BorderLayoutRegion} The new region
34206      */
34207     addRegion : function(config)
34208     {
34209         if(!this.regions[config.region]){
34210             var r = this.factory(config);
34211             this.bindRegion(r);
34212         }
34213         return this.regions[config.region];
34214     },
34215
34216     // private (kinda)
34217     bindRegion : function(r){
34218         this.regions[r.config.region] = r;
34219         
34220         r.on("visibilitychange",    this.layout, this);
34221         r.on("paneladded",          this.layout, this);
34222         r.on("panelremoved",        this.layout, this);
34223         r.on("invalidated",         this.layout, this);
34224         r.on("resized",             this.onRegionResized, this);
34225         r.on("collapsed",           this.onRegionCollapsed, this);
34226         r.on("expanded",            this.onRegionExpanded, this);
34227     },
34228
34229     /**
34230      * Performs a layout update.
34231      */
34232     layout : function()
34233     {
34234         if(this.updating) {
34235             return;
34236         }
34237         
34238         // render all the rebions if they have not been done alreayd?
34239         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34240             if(this.regions[region] && !this.regions[region].bodyEl){
34241                 this.regions[region].onRender(this.el)
34242             }
34243         },this);
34244         
34245         var size = this.getViewSize();
34246         var w = size.width;
34247         var h = size.height;
34248         var centerW = w;
34249         var centerH = h;
34250         var centerY = 0;
34251         var centerX = 0;
34252         //var x = 0, y = 0;
34253
34254         var rs = this.regions;
34255         var north = rs["north"];
34256         var south = rs["south"]; 
34257         var west = rs["west"];
34258         var east = rs["east"];
34259         var center = rs["center"];
34260         //if(this.hideOnLayout){ // not supported anymore
34261             //c.el.setStyle("display", "none");
34262         //}
34263         if(north && north.isVisible()){
34264             var b = north.getBox();
34265             var m = north.getMargins();
34266             b.width = w - (m.left+m.right);
34267             b.x = m.left;
34268             b.y = m.top;
34269             centerY = b.height + b.y + m.bottom;
34270             centerH -= centerY;
34271             north.updateBox(this.safeBox(b));
34272         }
34273         if(south && south.isVisible()){
34274             var b = south.getBox();
34275             var m = south.getMargins();
34276             b.width = w - (m.left+m.right);
34277             b.x = m.left;
34278             var totalHeight = (b.height + m.top + m.bottom);
34279             b.y = h - totalHeight + m.top;
34280             centerH -= totalHeight;
34281             south.updateBox(this.safeBox(b));
34282         }
34283         if(west && west.isVisible()){
34284             var b = west.getBox();
34285             var m = west.getMargins();
34286             b.height = centerH - (m.top+m.bottom);
34287             b.x = m.left;
34288             b.y = centerY + m.top;
34289             var totalWidth = (b.width + m.left + m.right);
34290             centerX += totalWidth;
34291             centerW -= totalWidth;
34292             west.updateBox(this.safeBox(b));
34293         }
34294         if(east && east.isVisible()){
34295             var b = east.getBox();
34296             var m = east.getMargins();
34297             b.height = centerH - (m.top+m.bottom);
34298             var totalWidth = (b.width + m.left + m.right);
34299             b.x = w - totalWidth + m.left;
34300             b.y = centerY + m.top;
34301             centerW -= totalWidth;
34302             east.updateBox(this.safeBox(b));
34303         }
34304         if(center){
34305             var m = center.getMargins();
34306             var centerBox = {
34307                 x: centerX + m.left,
34308                 y: centerY + m.top,
34309                 width: centerW - (m.left+m.right),
34310                 height: centerH - (m.top+m.bottom)
34311             };
34312             //if(this.hideOnLayout){
34313                 //center.el.setStyle("display", "block");
34314             //}
34315             center.updateBox(this.safeBox(centerBox));
34316         }
34317         this.el.repaint();
34318         this.fireEvent("layout", this);
34319     },
34320
34321     // private
34322     safeBox : function(box){
34323         box.width = Math.max(0, box.width);
34324         box.height = Math.max(0, box.height);
34325         return box;
34326     },
34327
34328     /**
34329      * Adds a ContentPanel (or subclass) to this layout.
34330      * @param {String} target The target region key (north, south, east, west or center).
34331      * @param {Roo.ContentPanel} panel The panel to add
34332      * @return {Roo.ContentPanel} The added panel
34333      */
34334     add : function(target, panel){
34335          
34336         target = target.toLowerCase();
34337         return this.regions[target].add(panel);
34338     },
34339
34340     /**
34341      * Remove a ContentPanel (or subclass) to this layout.
34342      * @param {String} target The target region key (north, south, east, west or center).
34343      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34344      * @return {Roo.ContentPanel} The removed panel
34345      */
34346     remove : function(target, panel){
34347         target = target.toLowerCase();
34348         return this.regions[target].remove(panel);
34349     },
34350
34351     /**
34352      * Searches all regions for a panel with the specified id
34353      * @param {String} panelId
34354      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34355      */
34356     findPanel : function(panelId){
34357         var rs = this.regions;
34358         for(var target in rs){
34359             if(typeof rs[target] != "function"){
34360                 var p = rs[target].getPanel(panelId);
34361                 if(p){
34362                     return p;
34363                 }
34364             }
34365         }
34366         return null;
34367     },
34368
34369     /**
34370      * Searches all regions for a panel with the specified id and activates (shows) it.
34371      * @param {String/ContentPanel} panelId The panels id or the panel itself
34372      * @return {Roo.ContentPanel} The shown panel or null
34373      */
34374     showPanel : function(panelId) {
34375       var rs = this.regions;
34376       for(var target in rs){
34377          var r = rs[target];
34378          if(typeof r != "function"){
34379             if(r.hasPanel(panelId)){
34380                return r.showPanel(panelId);
34381             }
34382          }
34383       }
34384       return null;
34385    },
34386
34387    /**
34388      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34389      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34390      */
34391    /*
34392     restoreState : function(provider){
34393         if(!provider){
34394             provider = Roo.state.Manager;
34395         }
34396         var sm = new Roo.LayoutStateManager();
34397         sm.init(this, provider);
34398     },
34399 */
34400  
34401  
34402     /**
34403      * Adds a xtype elements to the layout.
34404      * <pre><code>
34405
34406 layout.addxtype({
34407        xtype : 'ContentPanel',
34408        region: 'west',
34409        items: [ .... ]
34410    }
34411 );
34412
34413 layout.addxtype({
34414         xtype : 'NestedLayoutPanel',
34415         region: 'west',
34416         layout: {
34417            center: { },
34418            west: { }   
34419         },
34420         items : [ ... list of content panels or nested layout panels.. ]
34421    }
34422 );
34423 </code></pre>
34424      * @param {Object} cfg Xtype definition of item to add.
34425      */
34426     addxtype : function(cfg)
34427     {
34428         // basically accepts a pannel...
34429         // can accept a layout region..!?!?
34430         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34431         
34432         
34433         // theory?  children can only be panels??
34434         
34435         //if (!cfg.xtype.match(/Panel$/)) {
34436         //    return false;
34437         //}
34438         var ret = false;
34439         
34440         if (typeof(cfg.region) == 'undefined') {
34441             Roo.log("Failed to add Panel, region was not set");
34442             Roo.log(cfg);
34443             return false;
34444         }
34445         var region = cfg.region;
34446         delete cfg.region;
34447         
34448           
34449         var xitems = [];
34450         if (cfg.items) {
34451             xitems = cfg.items;
34452             delete cfg.items;
34453         }
34454         var nb = false;
34455         
34456         switch(cfg.xtype) 
34457         {
34458             case 'Content':  // ContentPanel (el, cfg)
34459             case 'Scroll':  // ContentPanel (el, cfg)
34460             case 'View': 
34461                 cfg.autoCreate = true;
34462                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34463                 //} else {
34464                 //    var el = this.el.createChild();
34465                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34466                 //}
34467                 
34468                 this.add(region, ret);
34469                 break;
34470             
34471             /*
34472             case 'TreePanel': // our new panel!
34473                 cfg.el = this.el.createChild();
34474                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34475                 this.add(region, ret);
34476                 break;
34477             */
34478             
34479             case 'Nest': 
34480                 // create a new Layout (which is  a Border Layout...
34481                 
34482                 var clayout = cfg.layout;
34483                 clayout.el  = this.el.createChild();
34484                 clayout.items   = clayout.items  || [];
34485                 
34486                 delete cfg.layout;
34487                 
34488                 // replace this exitems with the clayout ones..
34489                 xitems = clayout.items;
34490                  
34491                 // force background off if it's in center...
34492                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34493                     cfg.background = false;
34494                 }
34495                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34496                 
34497                 
34498                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34499                 //console.log('adding nested layout panel '  + cfg.toSource());
34500                 this.add(region, ret);
34501                 nb = {}; /// find first...
34502                 break;
34503             
34504             case 'Grid':
34505                 
34506                 // needs grid and region
34507                 
34508                 //var el = this.getRegion(region).el.createChild();
34509                 /*
34510                  *var el = this.el.createChild();
34511                 // create the grid first...
34512                 cfg.grid.container = el;
34513                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34514                 */
34515                 
34516                 if (region == 'center' && this.active ) {
34517                     cfg.background = false;
34518                 }
34519                 
34520                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34521                 
34522                 this.add(region, ret);
34523                 /*
34524                 if (cfg.background) {
34525                     // render grid on panel activation (if panel background)
34526                     ret.on('activate', function(gp) {
34527                         if (!gp.grid.rendered) {
34528                     //        gp.grid.render(el);
34529                         }
34530                     });
34531                 } else {
34532                   //  cfg.grid.render(el);
34533                 }
34534                 */
34535                 break;
34536            
34537            
34538             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34539                 // it was the old xcomponent building that caused this before.
34540                 // espeically if border is the top element in the tree.
34541                 ret = this;
34542                 break; 
34543                 
34544                     
34545                 
34546                 
34547                 
34548             default:
34549                 /*
34550                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34551                     
34552                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34553                     this.add(region, ret);
34554                 } else {
34555                 */
34556                     Roo.log(cfg);
34557                     throw "Can not add '" + cfg.xtype + "' to Border";
34558                     return null;
34559              
34560                                 
34561              
34562         }
34563         this.beginUpdate();
34564         // add children..
34565         var region = '';
34566         var abn = {};
34567         Roo.each(xitems, function(i)  {
34568             region = nb && i.region ? i.region : false;
34569             
34570             var add = ret.addxtype(i);
34571            
34572             if (region) {
34573                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34574                 if (!i.background) {
34575                     abn[region] = nb[region] ;
34576                 }
34577             }
34578             
34579         });
34580         this.endUpdate();
34581
34582         // make the last non-background panel active..
34583         //if (nb) { Roo.log(abn); }
34584         if (nb) {
34585             
34586             for(var r in abn) {
34587                 region = this.getRegion(r);
34588                 if (region) {
34589                     // tried using nb[r], but it does not work..
34590                      
34591                     region.showPanel(abn[r]);
34592                    
34593                 }
34594             }
34595         }
34596         return ret;
34597         
34598     },
34599     
34600     
34601 // private
34602     factory : function(cfg)
34603     {
34604         
34605         var validRegions = Roo.bootstrap.layout.Border.regions;
34606
34607         var target = cfg.region;
34608         cfg.mgr = this;
34609         
34610         var r = Roo.bootstrap.layout;
34611         Roo.log(target);
34612         switch(target){
34613             case "north":
34614                 return new r.North(cfg);
34615             case "south":
34616                 return new r.South(cfg);
34617             case "east":
34618                 return new r.East(cfg);
34619             case "west":
34620                 return new r.West(cfg);
34621             case "center":
34622                 return new r.Center(cfg);
34623         }
34624         throw 'Layout region "'+target+'" not supported.';
34625     }
34626     
34627     
34628 });
34629  /*
34630  * Based on:
34631  * Ext JS Library 1.1.1
34632  * Copyright(c) 2006-2007, Ext JS, LLC.
34633  *
34634  * Originally Released Under LGPL - original licence link has changed is not relivant.
34635  *
34636  * Fork - LGPL
34637  * <script type="text/javascript">
34638  */
34639  
34640 /**
34641  * @class Roo.bootstrap.layout.Basic
34642  * @extends Roo.util.Observable
34643  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34644  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34645  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34646  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34647  * @cfg {string}   region  the region that it inhabits..
34648  * @cfg {bool}   skipConfig skip config?
34649  * 
34650
34651  */
34652 Roo.bootstrap.layout.Basic = function(config){
34653     
34654     this.mgr = config.mgr;
34655     
34656     this.position = config.region;
34657     
34658     var skipConfig = config.skipConfig;
34659     
34660     this.events = {
34661         /**
34662          * @scope Roo.BasicLayoutRegion
34663          */
34664         
34665         /**
34666          * @event beforeremove
34667          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34668          * @param {Roo.LayoutRegion} this
34669          * @param {Roo.ContentPanel} panel The panel
34670          * @param {Object} e The cancel event object
34671          */
34672         "beforeremove" : true,
34673         /**
34674          * @event invalidated
34675          * Fires when the layout for this region is changed.
34676          * @param {Roo.LayoutRegion} this
34677          */
34678         "invalidated" : true,
34679         /**
34680          * @event visibilitychange
34681          * Fires when this region is shown or hidden 
34682          * @param {Roo.LayoutRegion} this
34683          * @param {Boolean} visibility true or false
34684          */
34685         "visibilitychange" : true,
34686         /**
34687          * @event paneladded
34688          * Fires when a panel is added. 
34689          * @param {Roo.LayoutRegion} this
34690          * @param {Roo.ContentPanel} panel The panel
34691          */
34692         "paneladded" : true,
34693         /**
34694          * @event panelremoved
34695          * Fires when a panel is removed. 
34696          * @param {Roo.LayoutRegion} this
34697          * @param {Roo.ContentPanel} panel The panel
34698          */
34699         "panelremoved" : true,
34700         /**
34701          * @event beforecollapse
34702          * Fires when this region before collapse.
34703          * @param {Roo.LayoutRegion} this
34704          */
34705         "beforecollapse" : true,
34706         /**
34707          * @event collapsed
34708          * Fires when this region is collapsed.
34709          * @param {Roo.LayoutRegion} this
34710          */
34711         "collapsed" : true,
34712         /**
34713          * @event expanded
34714          * Fires when this region is expanded.
34715          * @param {Roo.LayoutRegion} this
34716          */
34717         "expanded" : true,
34718         /**
34719          * @event slideshow
34720          * Fires when this region is slid into view.
34721          * @param {Roo.LayoutRegion} this
34722          */
34723         "slideshow" : true,
34724         /**
34725          * @event slidehide
34726          * Fires when this region slides out of view. 
34727          * @param {Roo.LayoutRegion} this
34728          */
34729         "slidehide" : true,
34730         /**
34731          * @event panelactivated
34732          * Fires when a panel is activated. 
34733          * @param {Roo.LayoutRegion} this
34734          * @param {Roo.ContentPanel} panel The activated panel
34735          */
34736         "panelactivated" : true,
34737         /**
34738          * @event resized
34739          * Fires when the user resizes this region. 
34740          * @param {Roo.LayoutRegion} this
34741          * @param {Number} newSize The new size (width for east/west, height for north/south)
34742          */
34743         "resized" : true
34744     };
34745     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34746     this.panels = new Roo.util.MixedCollection();
34747     this.panels.getKey = this.getPanelId.createDelegate(this);
34748     this.box = null;
34749     this.activePanel = null;
34750     // ensure listeners are added...
34751     
34752     if (config.listeners || config.events) {
34753         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34754             listeners : config.listeners || {},
34755             events : config.events || {}
34756         });
34757     }
34758     
34759     if(skipConfig !== true){
34760         this.applyConfig(config);
34761     }
34762 };
34763
34764 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34765 {
34766     getPanelId : function(p){
34767         return p.getId();
34768     },
34769     
34770     applyConfig : function(config){
34771         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34772         this.config = config;
34773         
34774     },
34775     
34776     /**
34777      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34778      * the width, for horizontal (north, south) the height.
34779      * @param {Number} newSize The new width or height
34780      */
34781     resizeTo : function(newSize){
34782         var el = this.el ? this.el :
34783                  (this.activePanel ? this.activePanel.getEl() : null);
34784         if(el){
34785             switch(this.position){
34786                 case "east":
34787                 case "west":
34788                     el.setWidth(newSize);
34789                     this.fireEvent("resized", this, newSize);
34790                 break;
34791                 case "north":
34792                 case "south":
34793                     el.setHeight(newSize);
34794                     this.fireEvent("resized", this, newSize);
34795                 break;                
34796             }
34797         }
34798     },
34799     
34800     getBox : function(){
34801         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34802     },
34803     
34804     getMargins : function(){
34805         return this.margins;
34806     },
34807     
34808     updateBox : function(box){
34809         this.box = box;
34810         var el = this.activePanel.getEl();
34811         el.dom.style.left = box.x + "px";
34812         el.dom.style.top = box.y + "px";
34813         this.activePanel.setSize(box.width, box.height);
34814     },
34815     
34816     /**
34817      * Returns the container element for this region.
34818      * @return {Roo.Element}
34819      */
34820     getEl : function(){
34821         return this.activePanel;
34822     },
34823     
34824     /**
34825      * Returns true if this region is currently visible.
34826      * @return {Boolean}
34827      */
34828     isVisible : function(){
34829         return this.activePanel ? true : false;
34830     },
34831     
34832     setActivePanel : function(panel){
34833         panel = this.getPanel(panel);
34834         if(this.activePanel && this.activePanel != panel){
34835             this.activePanel.setActiveState(false);
34836             this.activePanel.getEl().setLeftTop(-10000,-10000);
34837         }
34838         this.activePanel = panel;
34839         panel.setActiveState(true);
34840         if(this.box){
34841             panel.setSize(this.box.width, this.box.height);
34842         }
34843         this.fireEvent("panelactivated", this, panel);
34844         this.fireEvent("invalidated");
34845     },
34846     
34847     /**
34848      * Show the specified panel.
34849      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34850      * @return {Roo.ContentPanel} The shown panel or null
34851      */
34852     showPanel : function(panel){
34853         panel = this.getPanel(panel);
34854         if(panel){
34855             this.setActivePanel(panel);
34856         }
34857         return panel;
34858     },
34859     
34860     /**
34861      * Get the active panel for this region.
34862      * @return {Roo.ContentPanel} The active panel or null
34863      */
34864     getActivePanel : function(){
34865         return this.activePanel;
34866     },
34867     
34868     /**
34869      * Add the passed ContentPanel(s)
34870      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34871      * @return {Roo.ContentPanel} The panel added (if only one was added)
34872      */
34873     add : function(panel){
34874         if(arguments.length > 1){
34875             for(var i = 0, len = arguments.length; i < len; i++) {
34876                 this.add(arguments[i]);
34877             }
34878             return null;
34879         }
34880         if(this.hasPanel(panel)){
34881             this.showPanel(panel);
34882             return panel;
34883         }
34884         var el = panel.getEl();
34885         if(el.dom.parentNode != this.mgr.el.dom){
34886             this.mgr.el.dom.appendChild(el.dom);
34887         }
34888         if(panel.setRegion){
34889             panel.setRegion(this);
34890         }
34891         this.panels.add(panel);
34892         el.setStyle("position", "absolute");
34893         if(!panel.background){
34894             this.setActivePanel(panel);
34895             if(this.config.initialSize && this.panels.getCount()==1){
34896                 this.resizeTo(this.config.initialSize);
34897             }
34898         }
34899         this.fireEvent("paneladded", this, panel);
34900         return panel;
34901     },
34902     
34903     /**
34904      * Returns true if the panel is in this region.
34905      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34906      * @return {Boolean}
34907      */
34908     hasPanel : function(panel){
34909         if(typeof panel == "object"){ // must be panel obj
34910             panel = panel.getId();
34911         }
34912         return this.getPanel(panel) ? true : false;
34913     },
34914     
34915     /**
34916      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34917      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34918      * @param {Boolean} preservePanel Overrides the config preservePanel option
34919      * @return {Roo.ContentPanel} The panel that was removed
34920      */
34921     remove : function(panel, preservePanel){
34922         panel = this.getPanel(panel);
34923         if(!panel){
34924             return null;
34925         }
34926         var e = {};
34927         this.fireEvent("beforeremove", this, panel, e);
34928         if(e.cancel === true){
34929             return null;
34930         }
34931         var panelId = panel.getId();
34932         this.panels.removeKey(panelId);
34933         return panel;
34934     },
34935     
34936     /**
34937      * Returns the panel specified or null if it's not in this region.
34938      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34939      * @return {Roo.ContentPanel}
34940      */
34941     getPanel : function(id){
34942         if(typeof id == "object"){ // must be panel obj
34943             return id;
34944         }
34945         return this.panels.get(id);
34946     },
34947     
34948     /**
34949      * Returns this regions position (north/south/east/west/center).
34950      * @return {String} 
34951      */
34952     getPosition: function(){
34953         return this.position;    
34954     }
34955 });/*
34956  * Based on:
34957  * Ext JS Library 1.1.1
34958  * Copyright(c) 2006-2007, Ext JS, LLC.
34959  *
34960  * Originally Released Under LGPL - original licence link has changed is not relivant.
34961  *
34962  * Fork - LGPL
34963  * <script type="text/javascript">
34964  */
34965  
34966 /**
34967  * @class Roo.bootstrap.layout.Region
34968  * @extends Roo.bootstrap.layout.Basic
34969  * This class represents a region in a layout manager.
34970  
34971  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34972  * @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})
34973  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34974  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34975  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34976  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34977  * @cfg {String}    title           The title for the region (overrides panel titles)
34978  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34979  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34980  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34981  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34982  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34983  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34984  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34985  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34986  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34987  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34988
34989  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34990  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34991  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34992  * @cfg {Number}    width           For East/West panels
34993  * @cfg {Number}    height          For North/South panels
34994  * @cfg {Boolean}   split           To show the splitter
34995  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34996  * 
34997  * @cfg {string}   cls             Extra CSS classes to add to region
34998  * 
34999  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35000  * @cfg {string}   region  the region that it inhabits..
35001  *
35002
35003  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35004  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35005
35006  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35007  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35008  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35009  */
35010 Roo.bootstrap.layout.Region = function(config)
35011 {
35012     this.applyConfig(config);
35013
35014     var mgr = config.mgr;
35015     var pos = config.region;
35016     config.skipConfig = true;
35017     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35018     
35019     if (mgr.el) {
35020         this.onRender(mgr.el);   
35021     }
35022      
35023     this.visible = true;
35024     this.collapsed = false;
35025     this.unrendered_panels = [];
35026 };
35027
35028 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35029
35030     position: '', // set by wrapper (eg. north/south etc..)
35031     unrendered_panels : null,  // unrendered panels.
35032     createBody : function(){
35033         /** This region's body element 
35034         * @type Roo.Element */
35035         this.bodyEl = this.el.createChild({
35036                 tag: "div",
35037                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35038         });
35039     },
35040
35041     onRender: function(ctr, pos)
35042     {
35043         var dh = Roo.DomHelper;
35044         /** This region's container element 
35045         * @type Roo.Element */
35046         this.el = dh.append(ctr.dom, {
35047                 tag: "div",
35048                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35049             }, true);
35050         /** This region's title element 
35051         * @type Roo.Element */
35052     
35053         this.titleEl = dh.append(this.el.dom,
35054             {
35055                     tag: "div",
35056                     unselectable: "on",
35057                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35058                     children:[
35059                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35060                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35061                     ]}, true);
35062         
35063         this.titleEl.enableDisplayMode();
35064         /** This region's title text element 
35065         * @type HTMLElement */
35066         this.titleTextEl = this.titleEl.dom.firstChild;
35067         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35068         /*
35069         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35070         this.closeBtn.enableDisplayMode();
35071         this.closeBtn.on("click", this.closeClicked, this);
35072         this.closeBtn.hide();
35073     */
35074         this.createBody(this.config);
35075         if(this.config.hideWhenEmpty){
35076             this.hide();
35077             this.on("paneladded", this.validateVisibility, this);
35078             this.on("panelremoved", this.validateVisibility, this);
35079         }
35080         if(this.autoScroll){
35081             this.bodyEl.setStyle("overflow", "auto");
35082         }else{
35083             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35084         }
35085         //if(c.titlebar !== false){
35086             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35087                 this.titleEl.hide();
35088             }else{
35089                 this.titleEl.show();
35090                 if(this.config.title){
35091                     this.titleTextEl.innerHTML = this.config.title;
35092                 }
35093             }
35094         //}
35095         if(this.config.collapsed){
35096             this.collapse(true);
35097         }
35098         if(this.config.hidden){
35099             this.hide();
35100         }
35101         
35102         if (this.unrendered_panels && this.unrendered_panels.length) {
35103             for (var i =0;i< this.unrendered_panels.length; i++) {
35104                 this.add(this.unrendered_panels[i]);
35105             }
35106             this.unrendered_panels = null;
35107             
35108         }
35109         
35110     },
35111     
35112     applyConfig : function(c)
35113     {
35114         /*
35115          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35116             var dh = Roo.DomHelper;
35117             if(c.titlebar !== false){
35118                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35119                 this.collapseBtn.on("click", this.collapse, this);
35120                 this.collapseBtn.enableDisplayMode();
35121                 /*
35122                 if(c.showPin === true || this.showPin){
35123                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35124                     this.stickBtn.enableDisplayMode();
35125                     this.stickBtn.on("click", this.expand, this);
35126                     this.stickBtn.hide();
35127                 }
35128                 
35129             }
35130             */
35131             /** This region's collapsed element
35132             * @type Roo.Element */
35133             /*
35134              *
35135             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35136                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35137             ]}, true);
35138             
35139             if(c.floatable !== false){
35140                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35141                this.collapsedEl.on("click", this.collapseClick, this);
35142             }
35143
35144             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35145                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35146                    id: "message", unselectable: "on", style:{"float":"left"}});
35147                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35148              }
35149             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35150             this.expandBtn.on("click", this.expand, this);
35151             
35152         }
35153         
35154         if(this.collapseBtn){
35155             this.collapseBtn.setVisible(c.collapsible == true);
35156         }
35157         
35158         this.cmargins = c.cmargins || this.cmargins ||
35159                          (this.position == "west" || this.position == "east" ?
35160                              {top: 0, left: 2, right:2, bottom: 0} :
35161                              {top: 2, left: 0, right:0, bottom: 2});
35162         */
35163         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35164         
35165         
35166         this.bottomTabs = c.tabPosition != "top";
35167         
35168         this.autoScroll = c.autoScroll || false;
35169         
35170         
35171        
35172         
35173         this.duration = c.duration || .30;
35174         this.slideDuration = c.slideDuration || .45;
35175         this.config = c;
35176        
35177     },
35178     /**
35179      * Returns true if this region is currently visible.
35180      * @return {Boolean}
35181      */
35182     isVisible : function(){
35183         return this.visible;
35184     },
35185
35186     /**
35187      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35188      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35189      */
35190     //setCollapsedTitle : function(title){
35191     //    title = title || "&#160;";
35192      //   if(this.collapsedTitleTextEl){
35193       //      this.collapsedTitleTextEl.innerHTML = title;
35194        // }
35195     //},
35196
35197     getBox : function(){
35198         var b;
35199       //  if(!this.collapsed){
35200             b = this.el.getBox(false, true);
35201        // }else{
35202           //  b = this.collapsedEl.getBox(false, true);
35203         //}
35204         return b;
35205     },
35206
35207     getMargins : function(){
35208         return this.margins;
35209         //return this.collapsed ? this.cmargins : this.margins;
35210     },
35211 /*
35212     highlight : function(){
35213         this.el.addClass("x-layout-panel-dragover");
35214     },
35215
35216     unhighlight : function(){
35217         this.el.removeClass("x-layout-panel-dragover");
35218     },
35219 */
35220     updateBox : function(box)
35221     {
35222         if (!this.bodyEl) {
35223             return; // not rendered yet..
35224         }
35225         
35226         this.box = box;
35227         if(!this.collapsed){
35228             this.el.dom.style.left = box.x + "px";
35229             this.el.dom.style.top = box.y + "px";
35230             this.updateBody(box.width, box.height);
35231         }else{
35232             this.collapsedEl.dom.style.left = box.x + "px";
35233             this.collapsedEl.dom.style.top = box.y + "px";
35234             this.collapsedEl.setSize(box.width, box.height);
35235         }
35236         if(this.tabs){
35237             this.tabs.autoSizeTabs();
35238         }
35239     },
35240
35241     updateBody : function(w, h)
35242     {
35243         if(w !== null){
35244             this.el.setWidth(w);
35245             w -= this.el.getBorderWidth("rl");
35246             if(this.config.adjustments){
35247                 w += this.config.adjustments[0];
35248             }
35249         }
35250         if(h !== null && h > 0){
35251             this.el.setHeight(h);
35252             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35253             h -= this.el.getBorderWidth("tb");
35254             if(this.config.adjustments){
35255                 h += this.config.adjustments[1];
35256             }
35257             this.bodyEl.setHeight(h);
35258             if(this.tabs){
35259                 h = this.tabs.syncHeight(h);
35260             }
35261         }
35262         if(this.panelSize){
35263             w = w !== null ? w : this.panelSize.width;
35264             h = h !== null ? h : this.panelSize.height;
35265         }
35266         if(this.activePanel){
35267             var el = this.activePanel.getEl();
35268             w = w !== null ? w : el.getWidth();
35269             h = h !== null ? h : el.getHeight();
35270             this.panelSize = {width: w, height: h};
35271             this.activePanel.setSize(w, h);
35272         }
35273         if(Roo.isIE && this.tabs){
35274             this.tabs.el.repaint();
35275         }
35276     },
35277
35278     /**
35279      * Returns the container element for this region.
35280      * @return {Roo.Element}
35281      */
35282     getEl : function(){
35283         return this.el;
35284     },
35285
35286     /**
35287      * Hides this region.
35288      */
35289     hide : function(){
35290         //if(!this.collapsed){
35291             this.el.dom.style.left = "-2000px";
35292             this.el.hide();
35293         //}else{
35294          //   this.collapsedEl.dom.style.left = "-2000px";
35295          //   this.collapsedEl.hide();
35296        // }
35297         this.visible = false;
35298         this.fireEvent("visibilitychange", this, false);
35299     },
35300
35301     /**
35302      * Shows this region if it was previously hidden.
35303      */
35304     show : function(){
35305         //if(!this.collapsed){
35306             this.el.show();
35307         //}else{
35308         //    this.collapsedEl.show();
35309        // }
35310         this.visible = true;
35311         this.fireEvent("visibilitychange", this, true);
35312     },
35313 /*
35314     closeClicked : function(){
35315         if(this.activePanel){
35316             this.remove(this.activePanel);
35317         }
35318     },
35319
35320     collapseClick : function(e){
35321         if(this.isSlid){
35322            e.stopPropagation();
35323            this.slideIn();
35324         }else{
35325            e.stopPropagation();
35326            this.slideOut();
35327         }
35328     },
35329 */
35330     /**
35331      * Collapses this region.
35332      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35333      */
35334     /*
35335     collapse : function(skipAnim, skipCheck = false){
35336         if(this.collapsed) {
35337             return;
35338         }
35339         
35340         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35341             
35342             this.collapsed = true;
35343             if(this.split){
35344                 this.split.el.hide();
35345             }
35346             if(this.config.animate && skipAnim !== true){
35347                 this.fireEvent("invalidated", this);
35348                 this.animateCollapse();
35349             }else{
35350                 this.el.setLocation(-20000,-20000);
35351                 this.el.hide();
35352                 this.collapsedEl.show();
35353                 this.fireEvent("collapsed", this);
35354                 this.fireEvent("invalidated", this);
35355             }
35356         }
35357         
35358     },
35359 */
35360     animateCollapse : function(){
35361         // overridden
35362     },
35363
35364     /**
35365      * Expands this region if it was previously collapsed.
35366      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35367      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35368      */
35369     /*
35370     expand : function(e, skipAnim){
35371         if(e) {
35372             e.stopPropagation();
35373         }
35374         if(!this.collapsed || this.el.hasActiveFx()) {
35375             return;
35376         }
35377         if(this.isSlid){
35378             this.afterSlideIn();
35379             skipAnim = true;
35380         }
35381         this.collapsed = false;
35382         if(this.config.animate && skipAnim !== true){
35383             this.animateExpand();
35384         }else{
35385             this.el.show();
35386             if(this.split){
35387                 this.split.el.show();
35388             }
35389             this.collapsedEl.setLocation(-2000,-2000);
35390             this.collapsedEl.hide();
35391             this.fireEvent("invalidated", this);
35392             this.fireEvent("expanded", this);
35393         }
35394     },
35395 */
35396     animateExpand : function(){
35397         // overridden
35398     },
35399
35400     initTabs : function()
35401     {
35402         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35403         
35404         var ts = new Roo.bootstrap.panel.Tabs({
35405                 el: this.bodyEl.dom,
35406                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35407                 disableTooltips: this.config.disableTabTips,
35408                 toolbar : this.config.toolbar
35409             });
35410         
35411         if(this.config.hideTabs){
35412             ts.stripWrap.setDisplayed(false);
35413         }
35414         this.tabs = ts;
35415         ts.resizeTabs = this.config.resizeTabs === true;
35416         ts.minTabWidth = this.config.minTabWidth || 40;
35417         ts.maxTabWidth = this.config.maxTabWidth || 250;
35418         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35419         ts.monitorResize = false;
35420         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35421         ts.bodyEl.addClass('roo-layout-tabs-body');
35422         this.panels.each(this.initPanelAsTab, this);
35423     },
35424
35425     initPanelAsTab : function(panel){
35426         var ti = this.tabs.addTab(
35427             panel.getEl().id,
35428             panel.getTitle(),
35429             null,
35430             this.config.closeOnTab && panel.isClosable(),
35431             panel.tpl
35432         );
35433         if(panel.tabTip !== undefined){
35434             ti.setTooltip(panel.tabTip);
35435         }
35436         ti.on("activate", function(){
35437               this.setActivePanel(panel);
35438         }, this);
35439         
35440         if(this.config.closeOnTab){
35441             ti.on("beforeclose", function(t, e){
35442                 e.cancel = true;
35443                 this.remove(panel);
35444             }, this);
35445         }
35446         
35447         panel.tabItem = ti;
35448         
35449         return ti;
35450     },
35451
35452     updatePanelTitle : function(panel, title)
35453     {
35454         if(this.activePanel == panel){
35455             this.updateTitle(title);
35456         }
35457         if(this.tabs){
35458             var ti = this.tabs.getTab(panel.getEl().id);
35459             ti.setText(title);
35460             if(panel.tabTip !== undefined){
35461                 ti.setTooltip(panel.tabTip);
35462             }
35463         }
35464     },
35465
35466     updateTitle : function(title){
35467         if(this.titleTextEl && !this.config.title){
35468             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35469         }
35470     },
35471
35472     setActivePanel : function(panel)
35473     {
35474         panel = this.getPanel(panel);
35475         if(this.activePanel && this.activePanel != panel){
35476             if(this.activePanel.setActiveState(false) === false){
35477                 return;
35478             }
35479         }
35480         this.activePanel = panel;
35481         panel.setActiveState(true);
35482         if(this.panelSize){
35483             panel.setSize(this.panelSize.width, this.panelSize.height);
35484         }
35485         if(this.closeBtn){
35486             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35487         }
35488         this.updateTitle(panel.getTitle());
35489         if(this.tabs){
35490             this.fireEvent("invalidated", this);
35491         }
35492         this.fireEvent("panelactivated", this, panel);
35493     },
35494
35495     /**
35496      * Shows the specified panel.
35497      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35498      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35499      */
35500     showPanel : function(panel)
35501     {
35502         panel = this.getPanel(panel);
35503         if(panel){
35504             if(this.tabs){
35505                 var tab = this.tabs.getTab(panel.getEl().id);
35506                 if(tab.isHidden()){
35507                     this.tabs.unhideTab(tab.id);
35508                 }
35509                 tab.activate();
35510             }else{
35511                 this.setActivePanel(panel);
35512             }
35513         }
35514         return panel;
35515     },
35516
35517     /**
35518      * Get the active panel for this region.
35519      * @return {Roo.ContentPanel} The active panel or null
35520      */
35521     getActivePanel : function(){
35522         return this.activePanel;
35523     },
35524
35525     validateVisibility : function(){
35526         if(this.panels.getCount() < 1){
35527             this.updateTitle("&#160;");
35528             this.closeBtn.hide();
35529             this.hide();
35530         }else{
35531             if(!this.isVisible()){
35532                 this.show();
35533             }
35534         }
35535     },
35536
35537     /**
35538      * Adds the passed ContentPanel(s) to this region.
35539      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35540      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35541      */
35542     add : function(panel)
35543     {
35544         if(arguments.length > 1){
35545             for(var i = 0, len = arguments.length; i < len; i++) {
35546                 this.add(arguments[i]);
35547             }
35548             return null;
35549         }
35550         
35551         // if we have not been rendered yet, then we can not really do much of this..
35552         if (!this.bodyEl) {
35553             this.unrendered_panels.push(panel);
35554             return panel;
35555         }
35556         
35557         
35558         
35559         
35560         if(this.hasPanel(panel)){
35561             this.showPanel(panel);
35562             return panel;
35563         }
35564         panel.setRegion(this);
35565         this.panels.add(panel);
35566        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35567             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35568             // and hide them... ???
35569             this.bodyEl.dom.appendChild(panel.getEl().dom);
35570             if(panel.background !== true){
35571                 this.setActivePanel(panel);
35572             }
35573             this.fireEvent("paneladded", this, panel);
35574             return panel;
35575         }
35576         */
35577         if(!this.tabs){
35578             this.initTabs();
35579         }else{
35580             this.initPanelAsTab(panel);
35581         }
35582         
35583         
35584         if(panel.background !== true){
35585             this.tabs.activate(panel.getEl().id);
35586         }
35587         this.fireEvent("paneladded", this, panel);
35588         return panel;
35589     },
35590
35591     /**
35592      * Hides the tab for the specified panel.
35593      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35594      */
35595     hidePanel : function(panel){
35596         if(this.tabs && (panel = this.getPanel(panel))){
35597             this.tabs.hideTab(panel.getEl().id);
35598         }
35599     },
35600
35601     /**
35602      * Unhides the tab for a previously hidden panel.
35603      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35604      */
35605     unhidePanel : function(panel){
35606         if(this.tabs && (panel = this.getPanel(panel))){
35607             this.tabs.unhideTab(panel.getEl().id);
35608         }
35609     },
35610
35611     clearPanels : function(){
35612         while(this.panels.getCount() > 0){
35613              this.remove(this.panels.first());
35614         }
35615     },
35616
35617     /**
35618      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35619      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35620      * @param {Boolean} preservePanel Overrides the config preservePanel option
35621      * @return {Roo.ContentPanel} The panel that was removed
35622      */
35623     remove : function(panel, preservePanel)
35624     {
35625         panel = this.getPanel(panel);
35626         if(!panel){
35627             return null;
35628         }
35629         var e = {};
35630         this.fireEvent("beforeremove", this, panel, e);
35631         if(e.cancel === true){
35632             return null;
35633         }
35634         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35635         var panelId = panel.getId();
35636         this.panels.removeKey(panelId);
35637         if(preservePanel){
35638             document.body.appendChild(panel.getEl().dom);
35639         }
35640         if(this.tabs){
35641             this.tabs.removeTab(panel.getEl().id);
35642         }else if (!preservePanel){
35643             this.bodyEl.dom.removeChild(panel.getEl().dom);
35644         }
35645         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35646             var p = this.panels.first();
35647             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35648             tempEl.appendChild(p.getEl().dom);
35649             this.bodyEl.update("");
35650             this.bodyEl.dom.appendChild(p.getEl().dom);
35651             tempEl = null;
35652             this.updateTitle(p.getTitle());
35653             this.tabs = null;
35654             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35655             this.setActivePanel(p);
35656         }
35657         panel.setRegion(null);
35658         if(this.activePanel == panel){
35659             this.activePanel = null;
35660         }
35661         if(this.config.autoDestroy !== false && preservePanel !== true){
35662             try{panel.destroy();}catch(e){}
35663         }
35664         this.fireEvent("panelremoved", this, panel);
35665         return panel;
35666     },
35667
35668     /**
35669      * Returns the TabPanel component used by this region
35670      * @return {Roo.TabPanel}
35671      */
35672     getTabs : function(){
35673         return this.tabs;
35674     },
35675
35676     createTool : function(parentEl, className){
35677         var btn = Roo.DomHelper.append(parentEl, {
35678             tag: "div",
35679             cls: "x-layout-tools-button",
35680             children: [ {
35681                 tag: "div",
35682                 cls: "roo-layout-tools-button-inner " + className,
35683                 html: "&#160;"
35684             }]
35685         }, true);
35686         btn.addClassOnOver("roo-layout-tools-button-over");
35687         return btn;
35688     }
35689 });/*
35690  * Based on:
35691  * Ext JS Library 1.1.1
35692  * Copyright(c) 2006-2007, Ext JS, LLC.
35693  *
35694  * Originally Released Under LGPL - original licence link has changed is not relivant.
35695  *
35696  * Fork - LGPL
35697  * <script type="text/javascript">
35698  */
35699  
35700
35701
35702 /**
35703  * @class Roo.SplitLayoutRegion
35704  * @extends Roo.LayoutRegion
35705  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35706  */
35707 Roo.bootstrap.layout.Split = function(config){
35708     this.cursor = config.cursor;
35709     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35710 };
35711
35712 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35713 {
35714     splitTip : "Drag to resize.",
35715     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35716     useSplitTips : false,
35717
35718     applyConfig : function(config){
35719         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35720     },
35721     
35722     onRender : function(ctr,pos) {
35723         
35724         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35725         if(!this.config.split){
35726             return;
35727         }
35728         if(!this.split){
35729             
35730             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35731                             tag: "div",
35732                             id: this.el.id + "-split",
35733                             cls: "roo-layout-split roo-layout-split-"+this.position,
35734                             html: "&#160;"
35735             });
35736             /** The SplitBar for this region 
35737             * @type Roo.SplitBar */
35738             // does not exist yet...
35739             Roo.log([this.position, this.orientation]);
35740             
35741             this.split = new Roo.bootstrap.SplitBar({
35742                 dragElement : splitEl,
35743                 resizingElement: this.el,
35744                 orientation : this.orientation
35745             });
35746             
35747             this.split.on("moved", this.onSplitMove, this);
35748             this.split.useShim = this.config.useShim === true;
35749             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35750             if(this.useSplitTips){
35751                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35752             }
35753             //if(config.collapsible){
35754             //    this.split.el.on("dblclick", this.collapse,  this);
35755             //}
35756         }
35757         if(typeof this.config.minSize != "undefined"){
35758             this.split.minSize = this.config.minSize;
35759         }
35760         if(typeof this.config.maxSize != "undefined"){
35761             this.split.maxSize = this.config.maxSize;
35762         }
35763         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35764             this.hideSplitter();
35765         }
35766         
35767     },
35768
35769     getHMaxSize : function(){
35770          var cmax = this.config.maxSize || 10000;
35771          var center = this.mgr.getRegion("center");
35772          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35773     },
35774
35775     getVMaxSize : function(){
35776          var cmax = this.config.maxSize || 10000;
35777          var center = this.mgr.getRegion("center");
35778          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35779     },
35780
35781     onSplitMove : function(split, newSize){
35782         this.fireEvent("resized", this, newSize);
35783     },
35784     
35785     /** 
35786      * Returns the {@link Roo.SplitBar} for this region.
35787      * @return {Roo.SplitBar}
35788      */
35789     getSplitBar : function(){
35790         return this.split;
35791     },
35792     
35793     hide : function(){
35794         this.hideSplitter();
35795         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35796     },
35797
35798     hideSplitter : function(){
35799         if(this.split){
35800             this.split.el.setLocation(-2000,-2000);
35801             this.split.el.hide();
35802         }
35803     },
35804
35805     show : function(){
35806         if(this.split){
35807             this.split.el.show();
35808         }
35809         Roo.bootstrap.layout.Split.superclass.show.call(this);
35810     },
35811     
35812     beforeSlide: function(){
35813         if(Roo.isGecko){// firefox overflow auto bug workaround
35814             this.bodyEl.clip();
35815             if(this.tabs) {
35816                 this.tabs.bodyEl.clip();
35817             }
35818             if(this.activePanel){
35819                 this.activePanel.getEl().clip();
35820                 
35821                 if(this.activePanel.beforeSlide){
35822                     this.activePanel.beforeSlide();
35823                 }
35824             }
35825         }
35826     },
35827     
35828     afterSlide : function(){
35829         if(Roo.isGecko){// firefox overflow auto bug workaround
35830             this.bodyEl.unclip();
35831             if(this.tabs) {
35832                 this.tabs.bodyEl.unclip();
35833             }
35834             if(this.activePanel){
35835                 this.activePanel.getEl().unclip();
35836                 if(this.activePanel.afterSlide){
35837                     this.activePanel.afterSlide();
35838                 }
35839             }
35840         }
35841     },
35842
35843     initAutoHide : function(){
35844         if(this.autoHide !== false){
35845             if(!this.autoHideHd){
35846                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35847                 this.autoHideHd = {
35848                     "mouseout": function(e){
35849                         if(!e.within(this.el, true)){
35850                             st.delay(500);
35851                         }
35852                     },
35853                     "mouseover" : function(e){
35854                         st.cancel();
35855                     },
35856                     scope : this
35857                 };
35858             }
35859             this.el.on(this.autoHideHd);
35860         }
35861     },
35862
35863     clearAutoHide : function(){
35864         if(this.autoHide !== false){
35865             this.el.un("mouseout", this.autoHideHd.mouseout);
35866             this.el.un("mouseover", this.autoHideHd.mouseover);
35867         }
35868     },
35869
35870     clearMonitor : function(){
35871         Roo.get(document).un("click", this.slideInIf, this);
35872     },
35873
35874     // these names are backwards but not changed for compat
35875     slideOut : function(){
35876         if(this.isSlid || this.el.hasActiveFx()){
35877             return;
35878         }
35879         this.isSlid = true;
35880         if(this.collapseBtn){
35881             this.collapseBtn.hide();
35882         }
35883         this.closeBtnState = this.closeBtn.getStyle('display');
35884         this.closeBtn.hide();
35885         if(this.stickBtn){
35886             this.stickBtn.show();
35887         }
35888         this.el.show();
35889         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35890         this.beforeSlide();
35891         this.el.setStyle("z-index", 10001);
35892         this.el.slideIn(this.getSlideAnchor(), {
35893             callback: function(){
35894                 this.afterSlide();
35895                 this.initAutoHide();
35896                 Roo.get(document).on("click", this.slideInIf, this);
35897                 this.fireEvent("slideshow", this);
35898             },
35899             scope: this,
35900             block: true
35901         });
35902     },
35903
35904     afterSlideIn : function(){
35905         this.clearAutoHide();
35906         this.isSlid = false;
35907         this.clearMonitor();
35908         this.el.setStyle("z-index", "");
35909         if(this.collapseBtn){
35910             this.collapseBtn.show();
35911         }
35912         this.closeBtn.setStyle('display', this.closeBtnState);
35913         if(this.stickBtn){
35914             this.stickBtn.hide();
35915         }
35916         this.fireEvent("slidehide", this);
35917     },
35918
35919     slideIn : function(cb){
35920         if(!this.isSlid || this.el.hasActiveFx()){
35921             Roo.callback(cb);
35922             return;
35923         }
35924         this.isSlid = false;
35925         this.beforeSlide();
35926         this.el.slideOut(this.getSlideAnchor(), {
35927             callback: function(){
35928                 this.el.setLeftTop(-10000, -10000);
35929                 this.afterSlide();
35930                 this.afterSlideIn();
35931                 Roo.callback(cb);
35932             },
35933             scope: this,
35934             block: true
35935         });
35936     },
35937     
35938     slideInIf : function(e){
35939         if(!e.within(this.el)){
35940             this.slideIn();
35941         }
35942     },
35943
35944     animateCollapse : function(){
35945         this.beforeSlide();
35946         this.el.setStyle("z-index", 20000);
35947         var anchor = this.getSlideAnchor();
35948         this.el.slideOut(anchor, {
35949             callback : function(){
35950                 this.el.setStyle("z-index", "");
35951                 this.collapsedEl.slideIn(anchor, {duration:.3});
35952                 this.afterSlide();
35953                 this.el.setLocation(-10000,-10000);
35954                 this.el.hide();
35955                 this.fireEvent("collapsed", this);
35956             },
35957             scope: this,
35958             block: true
35959         });
35960     },
35961
35962     animateExpand : function(){
35963         this.beforeSlide();
35964         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35965         this.el.setStyle("z-index", 20000);
35966         this.collapsedEl.hide({
35967             duration:.1
35968         });
35969         this.el.slideIn(this.getSlideAnchor(), {
35970             callback : function(){
35971                 this.el.setStyle("z-index", "");
35972                 this.afterSlide();
35973                 if(this.split){
35974                     this.split.el.show();
35975                 }
35976                 this.fireEvent("invalidated", this);
35977                 this.fireEvent("expanded", this);
35978             },
35979             scope: this,
35980             block: true
35981         });
35982     },
35983
35984     anchors : {
35985         "west" : "left",
35986         "east" : "right",
35987         "north" : "top",
35988         "south" : "bottom"
35989     },
35990
35991     sanchors : {
35992         "west" : "l",
35993         "east" : "r",
35994         "north" : "t",
35995         "south" : "b"
35996     },
35997
35998     canchors : {
35999         "west" : "tl-tr",
36000         "east" : "tr-tl",
36001         "north" : "tl-bl",
36002         "south" : "bl-tl"
36003     },
36004
36005     getAnchor : function(){
36006         return this.anchors[this.position];
36007     },
36008
36009     getCollapseAnchor : function(){
36010         return this.canchors[this.position];
36011     },
36012
36013     getSlideAnchor : function(){
36014         return this.sanchors[this.position];
36015     },
36016
36017     getAlignAdj : function(){
36018         var cm = this.cmargins;
36019         switch(this.position){
36020             case "west":
36021                 return [0, 0];
36022             break;
36023             case "east":
36024                 return [0, 0];
36025             break;
36026             case "north":
36027                 return [0, 0];
36028             break;
36029             case "south":
36030                 return [0, 0];
36031             break;
36032         }
36033     },
36034
36035     getExpandAdj : function(){
36036         var c = this.collapsedEl, cm = this.cmargins;
36037         switch(this.position){
36038             case "west":
36039                 return [-(cm.right+c.getWidth()+cm.left), 0];
36040             break;
36041             case "east":
36042                 return [cm.right+c.getWidth()+cm.left, 0];
36043             break;
36044             case "north":
36045                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36046             break;
36047             case "south":
36048                 return [0, cm.top+cm.bottom+c.getHeight()];
36049             break;
36050         }
36051     }
36052 });/*
36053  * Based on:
36054  * Ext JS Library 1.1.1
36055  * Copyright(c) 2006-2007, Ext JS, LLC.
36056  *
36057  * Originally Released Under LGPL - original licence link has changed is not relivant.
36058  *
36059  * Fork - LGPL
36060  * <script type="text/javascript">
36061  */
36062 /*
36063  * These classes are private internal classes
36064  */
36065 Roo.bootstrap.layout.Center = function(config){
36066     config.region = "center";
36067     Roo.bootstrap.layout.Region.call(this, config);
36068     this.visible = true;
36069     this.minWidth = config.minWidth || 20;
36070     this.minHeight = config.minHeight || 20;
36071 };
36072
36073 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36074     hide : function(){
36075         // center panel can't be hidden
36076     },
36077     
36078     show : function(){
36079         // center panel can't be hidden
36080     },
36081     
36082     getMinWidth: function(){
36083         return this.minWidth;
36084     },
36085     
36086     getMinHeight: function(){
36087         return this.minHeight;
36088     }
36089 });
36090
36091
36092
36093
36094  
36095
36096
36097
36098
36099
36100 Roo.bootstrap.layout.North = function(config)
36101 {
36102     config.region = 'north';
36103     config.cursor = 'n-resize';
36104     
36105     Roo.bootstrap.layout.Split.call(this, config);
36106     
36107     
36108     if(this.split){
36109         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36110         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36111         this.split.el.addClass("roo-layout-split-v");
36112     }
36113     var size = config.initialSize || config.height;
36114     if(typeof size != "undefined"){
36115         this.el.setHeight(size);
36116     }
36117 };
36118 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36119 {
36120     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36121     
36122     
36123     
36124     getBox : function(){
36125         if(this.collapsed){
36126             return this.collapsedEl.getBox();
36127         }
36128         var box = this.el.getBox();
36129         if(this.split){
36130             box.height += this.split.el.getHeight();
36131         }
36132         return box;
36133     },
36134     
36135     updateBox : function(box){
36136         if(this.split && !this.collapsed){
36137             box.height -= this.split.el.getHeight();
36138             this.split.el.setLeft(box.x);
36139             this.split.el.setTop(box.y+box.height);
36140             this.split.el.setWidth(box.width);
36141         }
36142         if(this.collapsed){
36143             this.updateBody(box.width, null);
36144         }
36145         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36146     }
36147 });
36148
36149
36150
36151
36152
36153 Roo.bootstrap.layout.South = function(config){
36154     config.region = 'south';
36155     config.cursor = 's-resize';
36156     Roo.bootstrap.layout.Split.call(this, config);
36157     if(this.split){
36158         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36159         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36160         this.split.el.addClass("roo-layout-split-v");
36161     }
36162     var size = config.initialSize || config.height;
36163     if(typeof size != "undefined"){
36164         this.el.setHeight(size);
36165     }
36166 };
36167
36168 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36169     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36170     getBox : function(){
36171         if(this.collapsed){
36172             return this.collapsedEl.getBox();
36173         }
36174         var box = this.el.getBox();
36175         if(this.split){
36176             var sh = this.split.el.getHeight();
36177             box.height += sh;
36178             box.y -= sh;
36179         }
36180         return box;
36181     },
36182     
36183     updateBox : function(box){
36184         if(this.split && !this.collapsed){
36185             var sh = this.split.el.getHeight();
36186             box.height -= sh;
36187             box.y += sh;
36188             this.split.el.setLeft(box.x);
36189             this.split.el.setTop(box.y-sh);
36190             this.split.el.setWidth(box.width);
36191         }
36192         if(this.collapsed){
36193             this.updateBody(box.width, null);
36194         }
36195         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36196     }
36197 });
36198
36199 Roo.bootstrap.layout.East = function(config){
36200     config.region = "east";
36201     config.cursor = "e-resize";
36202     Roo.bootstrap.layout.Split.call(this, config);
36203     if(this.split){
36204         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36205         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36206         this.split.el.addClass("roo-layout-split-h");
36207     }
36208     var size = config.initialSize || config.width;
36209     if(typeof size != "undefined"){
36210         this.el.setWidth(size);
36211     }
36212 };
36213 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36214     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36215     getBox : function(){
36216         if(this.collapsed){
36217             return this.collapsedEl.getBox();
36218         }
36219         var box = this.el.getBox();
36220         if(this.split){
36221             var sw = this.split.el.getWidth();
36222             box.width += sw;
36223             box.x -= sw;
36224         }
36225         return box;
36226     },
36227
36228     updateBox : function(box){
36229         if(this.split && !this.collapsed){
36230             var sw = this.split.el.getWidth();
36231             box.width -= sw;
36232             this.split.el.setLeft(box.x);
36233             this.split.el.setTop(box.y);
36234             this.split.el.setHeight(box.height);
36235             box.x += sw;
36236         }
36237         if(this.collapsed){
36238             this.updateBody(null, box.height);
36239         }
36240         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36241     }
36242 });
36243
36244 Roo.bootstrap.layout.West = function(config){
36245     config.region = "west";
36246     config.cursor = "w-resize";
36247     
36248     Roo.bootstrap.layout.Split.call(this, config);
36249     if(this.split){
36250         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36251         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36252         this.split.el.addClass("roo-layout-split-h");
36253     }
36254     
36255 };
36256 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36257     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36258     
36259     onRender: function(ctr, pos)
36260     {
36261         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36262         var size = this.config.initialSize || this.config.width;
36263         if(typeof size != "undefined"){
36264             this.el.setWidth(size);
36265         }
36266     },
36267     
36268     getBox : function(){
36269         if(this.collapsed){
36270             return this.collapsedEl.getBox();
36271         }
36272         var box = this.el.getBox();
36273         if(this.split){
36274             box.width += this.split.el.getWidth();
36275         }
36276         return box;
36277     },
36278     
36279     updateBox : function(box){
36280         if(this.split && !this.collapsed){
36281             var sw = this.split.el.getWidth();
36282             box.width -= sw;
36283             this.split.el.setLeft(box.x+box.width);
36284             this.split.el.setTop(box.y);
36285             this.split.el.setHeight(box.height);
36286         }
36287         if(this.collapsed){
36288             this.updateBody(null, box.height);
36289         }
36290         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36291     }
36292 });
36293 Roo.namespace("Roo.bootstrap.panel");/*
36294  * Based on:
36295  * Ext JS Library 1.1.1
36296  * Copyright(c) 2006-2007, Ext JS, LLC.
36297  *
36298  * Originally Released Under LGPL - original licence link has changed is not relivant.
36299  *
36300  * Fork - LGPL
36301  * <script type="text/javascript">
36302  */
36303 /**
36304  * @class Roo.ContentPanel
36305  * @extends Roo.util.Observable
36306  * A basic ContentPanel element.
36307  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36308  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36309  * @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
36310  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36311  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36312  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36313  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36314  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36315  * @cfg {String} title          The title for this panel
36316  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36317  * @cfg {String} url            Calls {@link #setUrl} with this value
36318  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36319  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36320  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36321  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36322  * @cfg {Boolean} badges render the badges
36323
36324  * @constructor
36325  * Create a new ContentPanel.
36326  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36327  * @param {String/Object} config A string to set only the title or a config object
36328  * @param {String} content (optional) Set the HTML content for this panel
36329  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36330  */
36331 Roo.bootstrap.panel.Content = function( config){
36332     
36333     this.tpl = config.tpl || false;
36334     
36335     var el = config.el;
36336     var content = config.content;
36337
36338     if(config.autoCreate){ // xtype is available if this is called from factory
36339         el = Roo.id();
36340     }
36341     this.el = Roo.get(el);
36342     if(!this.el && config && config.autoCreate){
36343         if(typeof config.autoCreate == "object"){
36344             if(!config.autoCreate.id){
36345                 config.autoCreate.id = config.id||el;
36346             }
36347             this.el = Roo.DomHelper.append(document.body,
36348                         config.autoCreate, true);
36349         }else{
36350             var elcfg =  {   tag: "div",
36351                             cls: "roo-layout-inactive-content",
36352                             id: config.id||el
36353                             };
36354             if (config.html) {
36355                 elcfg.html = config.html;
36356                 
36357             }
36358                         
36359             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36360         }
36361     } 
36362     this.closable = false;
36363     this.loaded = false;
36364     this.active = false;
36365    
36366       
36367     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36368         
36369         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36370         
36371         this.wrapEl = this.el; //this.el.wrap();
36372         var ti = [];
36373         if (config.toolbar.items) {
36374             ti = config.toolbar.items ;
36375             delete config.toolbar.items ;
36376         }
36377         
36378         var nitems = [];
36379         this.toolbar.render(this.wrapEl, 'before');
36380         for(var i =0;i < ti.length;i++) {
36381           //  Roo.log(['add child', items[i]]);
36382             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36383         }
36384         this.toolbar.items = nitems;
36385         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36386         delete config.toolbar;
36387         
36388     }
36389     /*
36390     // xtype created footer. - not sure if will work as we normally have to render first..
36391     if (this.footer && !this.footer.el && this.footer.xtype) {
36392         if (!this.wrapEl) {
36393             this.wrapEl = this.el.wrap();
36394         }
36395     
36396         this.footer.container = this.wrapEl.createChild();
36397          
36398         this.footer = Roo.factory(this.footer, Roo);
36399         
36400     }
36401     */
36402     
36403      if(typeof config == "string"){
36404         this.title = config;
36405     }else{
36406         Roo.apply(this, config);
36407     }
36408     
36409     if(this.resizeEl){
36410         this.resizeEl = Roo.get(this.resizeEl, true);
36411     }else{
36412         this.resizeEl = this.el;
36413     }
36414     // handle view.xtype
36415     
36416  
36417     
36418     
36419     this.addEvents({
36420         /**
36421          * @event activate
36422          * Fires when this panel is activated. 
36423          * @param {Roo.ContentPanel} this
36424          */
36425         "activate" : true,
36426         /**
36427          * @event deactivate
36428          * Fires when this panel is activated. 
36429          * @param {Roo.ContentPanel} this
36430          */
36431         "deactivate" : true,
36432
36433         /**
36434          * @event resize
36435          * Fires when this panel is resized if fitToFrame is true.
36436          * @param {Roo.ContentPanel} this
36437          * @param {Number} width The width after any component adjustments
36438          * @param {Number} height The height after any component adjustments
36439          */
36440         "resize" : true,
36441         
36442          /**
36443          * @event render
36444          * Fires when this tab is created
36445          * @param {Roo.ContentPanel} this
36446          */
36447         "render" : true
36448         
36449         
36450         
36451     });
36452     
36453
36454     
36455     
36456     if(this.autoScroll){
36457         this.resizeEl.setStyle("overflow", "auto");
36458     } else {
36459         // fix randome scrolling
36460         //this.el.on('scroll', function() {
36461         //    Roo.log('fix random scolling');
36462         //    this.scrollTo('top',0); 
36463         //});
36464     }
36465     content = content || this.content;
36466     if(content){
36467         this.setContent(content);
36468     }
36469     if(config && config.url){
36470         this.setUrl(this.url, this.params, this.loadOnce);
36471     }
36472     
36473     
36474     
36475     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36476     
36477     if (this.view && typeof(this.view.xtype) != 'undefined') {
36478         this.view.el = this.el.appendChild(document.createElement("div"));
36479         this.view = Roo.factory(this.view); 
36480         this.view.render  &&  this.view.render(false, '');  
36481     }
36482     
36483     
36484     this.fireEvent('render', this);
36485 };
36486
36487 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36488     
36489     tabTip : '',
36490     
36491     setRegion : function(region){
36492         this.region = region;
36493         this.setActiveClass(region && !this.background);
36494     },
36495     
36496     
36497     setActiveClass: function(state)
36498     {
36499         if(state){
36500            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36501            this.el.setStyle('position','relative');
36502         }else{
36503            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36504            this.el.setStyle('position', 'absolute');
36505         } 
36506     },
36507     
36508     /**
36509      * Returns the toolbar for this Panel if one was configured. 
36510      * @return {Roo.Toolbar} 
36511      */
36512     getToolbar : function(){
36513         return this.toolbar;
36514     },
36515     
36516     setActiveState : function(active)
36517     {
36518         this.active = active;
36519         this.setActiveClass(active);
36520         if(!active){
36521             if(this.fireEvent("deactivate", this) === false){
36522                 return false;
36523             }
36524             return true;
36525         }
36526         this.fireEvent("activate", this);
36527         return true;
36528     },
36529     /**
36530      * Updates this panel's element
36531      * @param {String} content The new content
36532      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36533     */
36534     setContent : function(content, loadScripts){
36535         this.el.update(content, loadScripts);
36536     },
36537
36538     ignoreResize : function(w, h){
36539         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36540             return true;
36541         }else{
36542             this.lastSize = {width: w, height: h};
36543             return false;
36544         }
36545     },
36546     /**
36547      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36548      * @return {Roo.UpdateManager} The UpdateManager
36549      */
36550     getUpdateManager : function(){
36551         return this.el.getUpdateManager();
36552     },
36553      /**
36554      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36555      * @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:
36556 <pre><code>
36557 panel.load({
36558     url: "your-url.php",
36559     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36560     callback: yourFunction,
36561     scope: yourObject, //(optional scope)
36562     discardUrl: false,
36563     nocache: false,
36564     text: "Loading...",
36565     timeout: 30,
36566     scripts: false
36567 });
36568 </code></pre>
36569      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36570      * 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.
36571      * @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}
36572      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36573      * @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.
36574      * @return {Roo.ContentPanel} this
36575      */
36576     load : function(){
36577         var um = this.el.getUpdateManager();
36578         um.update.apply(um, arguments);
36579         return this;
36580     },
36581
36582
36583     /**
36584      * 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.
36585      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36586      * @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)
36587      * @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)
36588      * @return {Roo.UpdateManager} The UpdateManager
36589      */
36590     setUrl : function(url, params, loadOnce){
36591         if(this.refreshDelegate){
36592             this.removeListener("activate", this.refreshDelegate);
36593         }
36594         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36595         this.on("activate", this.refreshDelegate);
36596         return this.el.getUpdateManager();
36597     },
36598     
36599     _handleRefresh : function(url, params, loadOnce){
36600         if(!loadOnce || !this.loaded){
36601             var updater = this.el.getUpdateManager();
36602             updater.update(url, params, this._setLoaded.createDelegate(this));
36603         }
36604     },
36605     
36606     _setLoaded : function(){
36607         this.loaded = true;
36608     }, 
36609     
36610     /**
36611      * Returns this panel's id
36612      * @return {String} 
36613      */
36614     getId : function(){
36615         return this.el.id;
36616     },
36617     
36618     /** 
36619      * Returns this panel's element - used by regiosn to add.
36620      * @return {Roo.Element} 
36621      */
36622     getEl : function(){
36623         return this.wrapEl || this.el;
36624     },
36625     
36626    
36627     
36628     adjustForComponents : function(width, height)
36629     {
36630         //Roo.log('adjustForComponents ');
36631         if(this.resizeEl != this.el){
36632             width -= this.el.getFrameWidth('lr');
36633             height -= this.el.getFrameWidth('tb');
36634         }
36635         if(this.toolbar){
36636             var te = this.toolbar.getEl();
36637             te.setWidth(width);
36638             height -= te.getHeight();
36639         }
36640         if(this.footer){
36641             var te = this.footer.getEl();
36642             te.setWidth(width);
36643             height -= te.getHeight();
36644         }
36645         
36646         
36647         if(this.adjustments){
36648             width += this.adjustments[0];
36649             height += this.adjustments[1];
36650         }
36651         return {"width": width, "height": height};
36652     },
36653     
36654     setSize : function(width, height){
36655         if(this.fitToFrame && !this.ignoreResize(width, height)){
36656             if(this.fitContainer && this.resizeEl != this.el){
36657                 this.el.setSize(width, height);
36658             }
36659             var size = this.adjustForComponents(width, height);
36660             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36661             this.fireEvent('resize', this, size.width, size.height);
36662         }
36663     },
36664     
36665     /**
36666      * Returns this panel's title
36667      * @return {String} 
36668      */
36669     getTitle : function(){
36670         
36671         if (typeof(this.title) != 'object') {
36672             return this.title;
36673         }
36674         
36675         var t = '';
36676         for (var k in this.title) {
36677             if (!this.title.hasOwnProperty(k)) {
36678                 continue;
36679             }
36680             
36681             if (k.indexOf('-') >= 0) {
36682                 var s = k.split('-');
36683                 for (var i = 0; i<s.length; i++) {
36684                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36685                 }
36686             } else {
36687                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36688             }
36689         }
36690         return t;
36691     },
36692     
36693     /**
36694      * Set this panel's title
36695      * @param {String} title
36696      */
36697     setTitle : function(title){
36698         this.title = title;
36699         if(this.region){
36700             this.region.updatePanelTitle(this, title);
36701         }
36702     },
36703     
36704     /**
36705      * Returns true is this panel was configured to be closable
36706      * @return {Boolean} 
36707      */
36708     isClosable : function(){
36709         return this.closable;
36710     },
36711     
36712     beforeSlide : function(){
36713         this.el.clip();
36714         this.resizeEl.clip();
36715     },
36716     
36717     afterSlide : function(){
36718         this.el.unclip();
36719         this.resizeEl.unclip();
36720     },
36721     
36722     /**
36723      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36724      *   Will fail silently if the {@link #setUrl} method has not been called.
36725      *   This does not activate the panel, just updates its content.
36726      */
36727     refresh : function(){
36728         if(this.refreshDelegate){
36729            this.loaded = false;
36730            this.refreshDelegate();
36731         }
36732     },
36733     
36734     /**
36735      * Destroys this panel
36736      */
36737     destroy : function(){
36738         this.el.removeAllListeners();
36739         var tempEl = document.createElement("span");
36740         tempEl.appendChild(this.el.dom);
36741         tempEl.innerHTML = "";
36742         this.el.remove();
36743         this.el = null;
36744     },
36745     
36746     /**
36747      * form - if the content panel contains a form - this is a reference to it.
36748      * @type {Roo.form.Form}
36749      */
36750     form : false,
36751     /**
36752      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36753      *    This contains a reference to it.
36754      * @type {Roo.View}
36755      */
36756     view : false,
36757     
36758       /**
36759      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36760      * <pre><code>
36761
36762 layout.addxtype({
36763        xtype : 'Form',
36764        items: [ .... ]
36765    }
36766 );
36767
36768 </code></pre>
36769      * @param {Object} cfg Xtype definition of item to add.
36770      */
36771     
36772     
36773     getChildContainer: function () {
36774         return this.getEl();
36775     }
36776     
36777     
36778     /*
36779         var  ret = new Roo.factory(cfg);
36780         return ret;
36781         
36782         
36783         // add form..
36784         if (cfg.xtype.match(/^Form$/)) {
36785             
36786             var el;
36787             //if (this.footer) {
36788             //    el = this.footer.container.insertSibling(false, 'before');
36789             //} else {
36790                 el = this.el.createChild();
36791             //}
36792
36793             this.form = new  Roo.form.Form(cfg);
36794             
36795             
36796             if ( this.form.allItems.length) {
36797                 this.form.render(el.dom);
36798             }
36799             return this.form;
36800         }
36801         // should only have one of theses..
36802         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36803             // views.. should not be just added - used named prop 'view''
36804             
36805             cfg.el = this.el.appendChild(document.createElement("div"));
36806             // factory?
36807             
36808             var ret = new Roo.factory(cfg);
36809              
36810              ret.render && ret.render(false, ''); // render blank..
36811             this.view = ret;
36812             return ret;
36813         }
36814         return false;
36815     }
36816     \*/
36817 });
36818  
36819 /**
36820  * @class Roo.bootstrap.panel.Grid
36821  * @extends Roo.bootstrap.panel.Content
36822  * @constructor
36823  * Create a new GridPanel.
36824  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36825  * @param {Object} config A the config object
36826   
36827  */
36828
36829
36830
36831 Roo.bootstrap.panel.Grid = function(config)
36832 {
36833     
36834       
36835     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36836         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36837
36838     config.el = this.wrapper;
36839     //this.el = this.wrapper;
36840     
36841       if (config.container) {
36842         // ctor'ed from a Border/panel.grid
36843         
36844         
36845         this.wrapper.setStyle("overflow", "hidden");
36846         this.wrapper.addClass('roo-grid-container');
36847
36848     }
36849     
36850     
36851     if(config.toolbar){
36852         var tool_el = this.wrapper.createChild();    
36853         this.toolbar = Roo.factory(config.toolbar);
36854         var ti = [];
36855         if (config.toolbar.items) {
36856             ti = config.toolbar.items ;
36857             delete config.toolbar.items ;
36858         }
36859         
36860         var nitems = [];
36861         this.toolbar.render(tool_el);
36862         for(var i =0;i < ti.length;i++) {
36863           //  Roo.log(['add child', items[i]]);
36864             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36865         }
36866         this.toolbar.items = nitems;
36867         
36868         delete config.toolbar;
36869     }
36870     
36871     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36872     config.grid.scrollBody = true;;
36873     config.grid.monitorWindowResize = false; // turn off autosizing
36874     config.grid.autoHeight = false;
36875     config.grid.autoWidth = false;
36876     
36877     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36878     
36879     if (config.background) {
36880         // render grid on panel activation (if panel background)
36881         this.on('activate', function(gp) {
36882             if (!gp.grid.rendered) {
36883                 gp.grid.render(this.wrapper);
36884                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36885             }
36886         });
36887             
36888     } else {
36889         this.grid.render(this.wrapper);
36890         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36891
36892     }
36893     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36894     // ??? needed ??? config.el = this.wrapper;
36895     
36896     
36897     
36898   
36899     // xtype created footer. - not sure if will work as we normally have to render first..
36900     if (this.footer && !this.footer.el && this.footer.xtype) {
36901         
36902         var ctr = this.grid.getView().getFooterPanel(true);
36903         this.footer.dataSource = this.grid.dataSource;
36904         this.footer = Roo.factory(this.footer, Roo);
36905         this.footer.render(ctr);
36906         
36907     }
36908     
36909     
36910     
36911     
36912      
36913 };
36914
36915 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36916     getId : function(){
36917         return this.grid.id;
36918     },
36919     
36920     /**
36921      * Returns the grid for this panel
36922      * @return {Roo.bootstrap.Table} 
36923      */
36924     getGrid : function(){
36925         return this.grid;    
36926     },
36927     
36928     setSize : function(width, height){
36929         if(!this.ignoreResize(width, height)){
36930             var grid = this.grid;
36931             var size = this.adjustForComponents(width, height);
36932             var gridel = grid.getGridEl();
36933             gridel.setSize(size.width, size.height);
36934             /*
36935             var thd = grid.getGridEl().select('thead',true).first();
36936             var tbd = grid.getGridEl().select('tbody', true).first();
36937             if (tbd) {
36938                 tbd.setSize(width, height - thd.getHeight());
36939             }
36940             */
36941             grid.autoSize();
36942         }
36943     },
36944      
36945     
36946     
36947     beforeSlide : function(){
36948         this.grid.getView().scroller.clip();
36949     },
36950     
36951     afterSlide : function(){
36952         this.grid.getView().scroller.unclip();
36953     },
36954     
36955     destroy : function(){
36956         this.grid.destroy();
36957         delete this.grid;
36958         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36959     }
36960 });
36961
36962 /**
36963  * @class Roo.bootstrap.panel.Nest
36964  * @extends Roo.bootstrap.panel.Content
36965  * @constructor
36966  * Create a new Panel, that can contain a layout.Border.
36967  * 
36968  * 
36969  * @param {Roo.BorderLayout} layout The layout for this panel
36970  * @param {String/Object} config A string to set only the title or a config object
36971  */
36972 Roo.bootstrap.panel.Nest = function(config)
36973 {
36974     // construct with only one argument..
36975     /* FIXME - implement nicer consturctors
36976     if (layout.layout) {
36977         config = layout;
36978         layout = config.layout;
36979         delete config.layout;
36980     }
36981     if (layout.xtype && !layout.getEl) {
36982         // then layout needs constructing..
36983         layout = Roo.factory(layout, Roo);
36984     }
36985     */
36986     
36987     config.el =  config.layout.getEl();
36988     
36989     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36990     
36991     config.layout.monitorWindowResize = false; // turn off autosizing
36992     this.layout = config.layout;
36993     this.layout.getEl().addClass("roo-layout-nested-layout");
36994     
36995     
36996     
36997     
36998 };
36999
37000 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37001
37002     setSize : function(width, height){
37003         if(!this.ignoreResize(width, height)){
37004             var size = this.adjustForComponents(width, height);
37005             var el = this.layout.getEl();
37006             if (size.height < 1) {
37007                 el.setWidth(size.width);   
37008             } else {
37009                 el.setSize(size.width, size.height);
37010             }
37011             var touch = el.dom.offsetWidth;
37012             this.layout.layout();
37013             // ie requires a double layout on the first pass
37014             if(Roo.isIE && !this.initialized){
37015                 this.initialized = true;
37016                 this.layout.layout();
37017             }
37018         }
37019     },
37020     
37021     // activate all subpanels if not currently active..
37022     
37023     setActiveState : function(active){
37024         this.active = active;
37025         this.setActiveClass(active);
37026         
37027         if(!active){
37028             this.fireEvent("deactivate", this);
37029             return;
37030         }
37031         
37032         this.fireEvent("activate", this);
37033         // not sure if this should happen before or after..
37034         if (!this.layout) {
37035             return; // should not happen..
37036         }
37037         var reg = false;
37038         for (var r in this.layout.regions) {
37039             reg = this.layout.getRegion(r);
37040             if (reg.getActivePanel()) {
37041                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37042                 reg.setActivePanel(reg.getActivePanel());
37043                 continue;
37044             }
37045             if (!reg.panels.length) {
37046                 continue;
37047             }
37048             reg.showPanel(reg.getPanel(0));
37049         }
37050         
37051         
37052         
37053         
37054     },
37055     
37056     /**
37057      * Returns the nested BorderLayout for this panel
37058      * @return {Roo.BorderLayout} 
37059      */
37060     getLayout : function(){
37061         return this.layout;
37062     },
37063     
37064      /**
37065      * Adds a xtype elements to the layout of the nested panel
37066      * <pre><code>
37067
37068 panel.addxtype({
37069        xtype : 'ContentPanel',
37070        region: 'west',
37071        items: [ .... ]
37072    }
37073 );
37074
37075 panel.addxtype({
37076         xtype : 'NestedLayoutPanel',
37077         region: 'west',
37078         layout: {
37079            center: { },
37080            west: { }   
37081         },
37082         items : [ ... list of content panels or nested layout panels.. ]
37083    }
37084 );
37085 </code></pre>
37086      * @param {Object} cfg Xtype definition of item to add.
37087      */
37088     addxtype : function(cfg) {
37089         return this.layout.addxtype(cfg);
37090     
37091     }
37092 });        /*
37093  * Based on:
37094  * Ext JS Library 1.1.1
37095  * Copyright(c) 2006-2007, Ext JS, LLC.
37096  *
37097  * Originally Released Under LGPL - original licence link has changed is not relivant.
37098  *
37099  * Fork - LGPL
37100  * <script type="text/javascript">
37101  */
37102 /**
37103  * @class Roo.TabPanel
37104  * @extends Roo.util.Observable
37105  * A lightweight tab container.
37106  * <br><br>
37107  * Usage:
37108  * <pre><code>
37109 // basic tabs 1, built from existing content
37110 var tabs = new Roo.TabPanel("tabs1");
37111 tabs.addTab("script", "View Script");
37112 tabs.addTab("markup", "View Markup");
37113 tabs.activate("script");
37114
37115 // more advanced tabs, built from javascript
37116 var jtabs = new Roo.TabPanel("jtabs");
37117 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37118
37119 // set up the UpdateManager
37120 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37121 var updater = tab2.getUpdateManager();
37122 updater.setDefaultUrl("ajax1.htm");
37123 tab2.on('activate', updater.refresh, updater, true);
37124
37125 // Use setUrl for Ajax loading
37126 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37127 tab3.setUrl("ajax2.htm", null, true);
37128
37129 // Disabled tab
37130 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37131 tab4.disable();
37132
37133 jtabs.activate("jtabs-1");
37134  * </code></pre>
37135  * @constructor
37136  * Create a new TabPanel.
37137  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37138  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37139  */
37140 Roo.bootstrap.panel.Tabs = function(config){
37141     /**
37142     * The container element for this TabPanel.
37143     * @type Roo.Element
37144     */
37145     this.el = Roo.get(config.el);
37146     delete config.el;
37147     if(config){
37148         if(typeof config == "boolean"){
37149             this.tabPosition = config ? "bottom" : "top";
37150         }else{
37151             Roo.apply(this, config);
37152         }
37153     }
37154     
37155     if(this.tabPosition == "bottom"){
37156         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37157         this.el.addClass("roo-tabs-bottom");
37158     }
37159     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37160     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37161     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37162     if(Roo.isIE){
37163         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37164     }
37165     if(this.tabPosition != "bottom"){
37166         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37167          * @type Roo.Element
37168          */
37169         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37170         this.el.addClass("roo-tabs-top");
37171     }
37172     this.items = [];
37173
37174     this.bodyEl.setStyle("position", "relative");
37175
37176     this.active = null;
37177     this.activateDelegate = this.activate.createDelegate(this);
37178
37179     this.addEvents({
37180         /**
37181          * @event tabchange
37182          * Fires when the active tab changes
37183          * @param {Roo.TabPanel} this
37184          * @param {Roo.TabPanelItem} activePanel The new active tab
37185          */
37186         "tabchange": true,
37187         /**
37188          * @event beforetabchange
37189          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37190          * @param {Roo.TabPanel} this
37191          * @param {Object} e Set cancel to true on this object to cancel the tab change
37192          * @param {Roo.TabPanelItem} tab The tab being changed to
37193          */
37194         "beforetabchange" : true
37195     });
37196
37197     Roo.EventManager.onWindowResize(this.onResize, this);
37198     this.cpad = this.el.getPadding("lr");
37199     this.hiddenCount = 0;
37200
37201
37202     // toolbar on the tabbar support...
37203     if (this.toolbar) {
37204         alert("no toolbar support yet");
37205         this.toolbar  = false;
37206         /*
37207         var tcfg = this.toolbar;
37208         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37209         this.toolbar = new Roo.Toolbar(tcfg);
37210         if (Roo.isSafari) {
37211             var tbl = tcfg.container.child('table', true);
37212             tbl.setAttribute('width', '100%');
37213         }
37214         */
37215         
37216     }
37217    
37218
37219
37220     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37221 };
37222
37223 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37224     /*
37225      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37226      */
37227     tabPosition : "top",
37228     /*
37229      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37230      */
37231     currentTabWidth : 0,
37232     /*
37233      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37234      */
37235     minTabWidth : 40,
37236     /*
37237      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37238      */
37239     maxTabWidth : 250,
37240     /*
37241      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37242      */
37243     preferredTabWidth : 175,
37244     /*
37245      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37246      */
37247     resizeTabs : false,
37248     /*
37249      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37250      */
37251     monitorResize : true,
37252     /*
37253      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37254      */
37255     toolbar : false,
37256
37257     /**
37258      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37259      * @param {String} id The id of the div to use <b>or create</b>
37260      * @param {String} text The text for the tab
37261      * @param {String} content (optional) Content to put in the TabPanelItem body
37262      * @param {Boolean} closable (optional) True to create a close icon on the tab
37263      * @return {Roo.TabPanelItem} The created TabPanelItem
37264      */
37265     addTab : function(id, text, content, closable, tpl)
37266     {
37267         var item = new Roo.bootstrap.panel.TabItem({
37268             panel: this,
37269             id : id,
37270             text : text,
37271             closable : closable,
37272             tpl : tpl
37273         });
37274         this.addTabItem(item);
37275         if(content){
37276             item.setContent(content);
37277         }
37278         return item;
37279     },
37280
37281     /**
37282      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37283      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37284      * @return {Roo.TabPanelItem}
37285      */
37286     getTab : function(id){
37287         return this.items[id];
37288     },
37289
37290     /**
37291      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37292      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37293      */
37294     hideTab : function(id){
37295         var t = this.items[id];
37296         if(!t.isHidden()){
37297            t.setHidden(true);
37298            this.hiddenCount++;
37299            this.autoSizeTabs();
37300         }
37301     },
37302
37303     /**
37304      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37305      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37306      */
37307     unhideTab : function(id){
37308         var t = this.items[id];
37309         if(t.isHidden()){
37310            t.setHidden(false);
37311            this.hiddenCount--;
37312            this.autoSizeTabs();
37313         }
37314     },
37315
37316     /**
37317      * Adds an existing {@link Roo.TabPanelItem}.
37318      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37319      */
37320     addTabItem : function(item){
37321         this.items[item.id] = item;
37322         this.items.push(item);
37323       //  if(this.resizeTabs){
37324     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37325   //         this.autoSizeTabs();
37326 //        }else{
37327 //            item.autoSize();
37328        // }
37329     },
37330
37331     /**
37332      * Removes a {@link Roo.TabPanelItem}.
37333      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37334      */
37335     removeTab : function(id){
37336         var items = this.items;
37337         var tab = items[id];
37338         if(!tab) { return; }
37339         var index = items.indexOf(tab);
37340         if(this.active == tab && items.length > 1){
37341             var newTab = this.getNextAvailable(index);
37342             if(newTab) {
37343                 newTab.activate();
37344             }
37345         }
37346         this.stripEl.dom.removeChild(tab.pnode.dom);
37347         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37348             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37349         }
37350         items.splice(index, 1);
37351         delete this.items[tab.id];
37352         tab.fireEvent("close", tab);
37353         tab.purgeListeners();
37354         this.autoSizeTabs();
37355     },
37356
37357     getNextAvailable : function(start){
37358         var items = this.items;
37359         var index = start;
37360         // look for a next tab that will slide over to
37361         // replace the one being removed
37362         while(index < items.length){
37363             var item = items[++index];
37364             if(item && !item.isHidden()){
37365                 return item;
37366             }
37367         }
37368         // if one isn't found select the previous tab (on the left)
37369         index = start;
37370         while(index >= 0){
37371             var item = items[--index];
37372             if(item && !item.isHidden()){
37373                 return item;
37374             }
37375         }
37376         return null;
37377     },
37378
37379     /**
37380      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37381      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37382      */
37383     disableTab : function(id){
37384         var tab = this.items[id];
37385         if(tab && this.active != tab){
37386             tab.disable();
37387         }
37388     },
37389
37390     /**
37391      * Enables a {@link Roo.TabPanelItem} that is disabled.
37392      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37393      */
37394     enableTab : function(id){
37395         var tab = this.items[id];
37396         tab.enable();
37397     },
37398
37399     /**
37400      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37401      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37402      * @return {Roo.TabPanelItem} The TabPanelItem.
37403      */
37404     activate : function(id){
37405         var tab = this.items[id];
37406         if(!tab){
37407             return null;
37408         }
37409         if(tab == this.active || tab.disabled){
37410             return tab;
37411         }
37412         var e = {};
37413         this.fireEvent("beforetabchange", this, e, tab);
37414         if(e.cancel !== true && !tab.disabled){
37415             if(this.active){
37416                 this.active.hide();
37417             }
37418             this.active = this.items[id];
37419             this.active.show();
37420             this.fireEvent("tabchange", this, this.active);
37421         }
37422         return tab;
37423     },
37424
37425     /**
37426      * Gets the active {@link Roo.TabPanelItem}.
37427      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37428      */
37429     getActiveTab : function(){
37430         return this.active;
37431     },
37432
37433     /**
37434      * Updates the tab body element to fit the height of the container element
37435      * for overflow scrolling
37436      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37437      */
37438     syncHeight : function(targetHeight){
37439         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37440         var bm = this.bodyEl.getMargins();
37441         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37442         this.bodyEl.setHeight(newHeight);
37443         return newHeight;
37444     },
37445
37446     onResize : function(){
37447         if(this.monitorResize){
37448             this.autoSizeTabs();
37449         }
37450     },
37451
37452     /**
37453      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37454      */
37455     beginUpdate : function(){
37456         this.updating = true;
37457     },
37458
37459     /**
37460      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37461      */
37462     endUpdate : function(){
37463         this.updating = false;
37464         this.autoSizeTabs();
37465     },
37466
37467     /**
37468      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37469      */
37470     autoSizeTabs : function(){
37471         var count = this.items.length;
37472         var vcount = count - this.hiddenCount;
37473         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37474             return;
37475         }
37476         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37477         var availWidth = Math.floor(w / vcount);
37478         var b = this.stripBody;
37479         if(b.getWidth() > w){
37480             var tabs = this.items;
37481             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37482             if(availWidth < this.minTabWidth){
37483                 /*if(!this.sleft){    // incomplete scrolling code
37484                     this.createScrollButtons();
37485                 }
37486                 this.showScroll();
37487                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37488             }
37489         }else{
37490             if(this.currentTabWidth < this.preferredTabWidth){
37491                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37492             }
37493         }
37494     },
37495
37496     /**
37497      * Returns the number of tabs in this TabPanel.
37498      * @return {Number}
37499      */
37500      getCount : function(){
37501          return this.items.length;
37502      },
37503
37504     /**
37505      * Resizes all the tabs to the passed width
37506      * @param {Number} The new width
37507      */
37508     setTabWidth : function(width){
37509         this.currentTabWidth = width;
37510         for(var i = 0, len = this.items.length; i < len; i++) {
37511                 if(!this.items[i].isHidden()) {
37512                 this.items[i].setWidth(width);
37513             }
37514         }
37515     },
37516
37517     /**
37518      * Destroys this TabPanel
37519      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37520      */
37521     destroy : function(removeEl){
37522         Roo.EventManager.removeResizeListener(this.onResize, this);
37523         for(var i = 0, len = this.items.length; i < len; i++){
37524             this.items[i].purgeListeners();
37525         }
37526         if(removeEl === true){
37527             this.el.update("");
37528             this.el.remove();
37529         }
37530     },
37531     
37532     createStrip : function(container)
37533     {
37534         var strip = document.createElement("nav");
37535         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37536         container.appendChild(strip);
37537         return strip;
37538     },
37539     
37540     createStripList : function(strip)
37541     {
37542         // div wrapper for retard IE
37543         // returns the "tr" element.
37544         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37545         //'<div class="x-tabs-strip-wrap">'+
37546           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37547           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37548         return strip.firstChild; //.firstChild.firstChild.firstChild;
37549     },
37550     createBody : function(container)
37551     {
37552         var body = document.createElement("div");
37553         Roo.id(body, "tab-body");
37554         //Roo.fly(body).addClass("x-tabs-body");
37555         Roo.fly(body).addClass("tab-content");
37556         container.appendChild(body);
37557         return body;
37558     },
37559     createItemBody :function(bodyEl, id){
37560         var body = Roo.getDom(id);
37561         if(!body){
37562             body = document.createElement("div");
37563             body.id = id;
37564         }
37565         //Roo.fly(body).addClass("x-tabs-item-body");
37566         Roo.fly(body).addClass("tab-pane");
37567          bodyEl.insertBefore(body, bodyEl.firstChild);
37568         return body;
37569     },
37570     /** @private */
37571     createStripElements :  function(stripEl, text, closable, tpl)
37572     {
37573         var td = document.createElement("li"); // was td..
37574         
37575         
37576         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37577         
37578         
37579         stripEl.appendChild(td);
37580         /*if(closable){
37581             td.className = "x-tabs-closable";
37582             if(!this.closeTpl){
37583                 this.closeTpl = new Roo.Template(
37584                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37585                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37586                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37587                 );
37588             }
37589             var el = this.closeTpl.overwrite(td, {"text": text});
37590             var close = el.getElementsByTagName("div")[0];
37591             var inner = el.getElementsByTagName("em")[0];
37592             return {"el": el, "close": close, "inner": inner};
37593         } else {
37594         */
37595         // not sure what this is..
37596 //            if(!this.tabTpl){
37597                 //this.tabTpl = new Roo.Template(
37598                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37599                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37600                 //);
37601 //                this.tabTpl = new Roo.Template(
37602 //                   '<a href="#">' +
37603 //                   '<span unselectable="on"' +
37604 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37605 //                            ' >{text}</span></a>'
37606 //                );
37607 //                
37608 //            }
37609
37610
37611             var template = tpl || this.tabTpl || false;
37612             
37613             if(!template){
37614                 
37615                 template = new Roo.Template(
37616                    '<a href="#">' +
37617                    '<span unselectable="on"' +
37618                             (this.disableTooltips ? '' : ' title="{text}"') +
37619                             ' >{text}</span></a>'
37620                 );
37621             }
37622             
37623             switch (typeof(template)) {
37624                 case 'object' :
37625                     break;
37626                 case 'string' :
37627                     template = new Roo.Template(template);
37628                     break;
37629                 default :
37630                     break;
37631             }
37632             
37633             var el = template.overwrite(td, {"text": text});
37634             
37635             var inner = el.getElementsByTagName("span")[0];
37636             
37637             return {"el": el, "inner": inner};
37638             
37639     }
37640         
37641     
37642 });
37643
37644 /**
37645  * @class Roo.TabPanelItem
37646  * @extends Roo.util.Observable
37647  * Represents an individual item (tab plus body) in a TabPanel.
37648  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37649  * @param {String} id The id of this TabPanelItem
37650  * @param {String} text The text for the tab of this TabPanelItem
37651  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37652  */
37653 Roo.bootstrap.panel.TabItem = function(config){
37654     /**
37655      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37656      * @type Roo.TabPanel
37657      */
37658     this.tabPanel = config.panel;
37659     /**
37660      * The id for this TabPanelItem
37661      * @type String
37662      */
37663     this.id = config.id;
37664     /** @private */
37665     this.disabled = false;
37666     /** @private */
37667     this.text = config.text;
37668     /** @private */
37669     this.loaded = false;
37670     this.closable = config.closable;
37671
37672     /**
37673      * The body element for this TabPanelItem.
37674      * @type Roo.Element
37675      */
37676     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37677     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37678     this.bodyEl.setStyle("display", "block");
37679     this.bodyEl.setStyle("zoom", "1");
37680     //this.hideAction();
37681
37682     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37683     /** @private */
37684     this.el = Roo.get(els.el);
37685     this.inner = Roo.get(els.inner, true);
37686     this.textEl = Roo.get(this.el.dom.firstChild, true);
37687     this.pnode = Roo.get(els.el.parentNode, true);
37688 //    this.el.on("mousedown", this.onTabMouseDown, this);
37689     this.el.on("click", this.onTabClick, this);
37690     /** @private */
37691     if(config.closable){
37692         var c = Roo.get(els.close, true);
37693         c.dom.title = this.closeText;
37694         c.addClassOnOver("close-over");
37695         c.on("click", this.closeClick, this);
37696      }
37697
37698     this.addEvents({
37699          /**
37700          * @event activate
37701          * Fires when this tab becomes the active tab.
37702          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37703          * @param {Roo.TabPanelItem} this
37704          */
37705         "activate": true,
37706         /**
37707          * @event beforeclose
37708          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37709          * @param {Roo.TabPanelItem} this
37710          * @param {Object} e Set cancel to true on this object to cancel the close.
37711          */
37712         "beforeclose": true,
37713         /**
37714          * @event close
37715          * Fires when this tab is closed.
37716          * @param {Roo.TabPanelItem} this
37717          */
37718          "close": true,
37719         /**
37720          * @event deactivate
37721          * Fires when this tab is no longer the active tab.
37722          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37723          * @param {Roo.TabPanelItem} this
37724          */
37725          "deactivate" : true
37726     });
37727     this.hidden = false;
37728
37729     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37730 };
37731
37732 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37733            {
37734     purgeListeners : function(){
37735        Roo.util.Observable.prototype.purgeListeners.call(this);
37736        this.el.removeAllListeners();
37737     },
37738     /**
37739      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37740      */
37741     show : function(){
37742         this.pnode.addClass("active");
37743         this.showAction();
37744         if(Roo.isOpera){
37745             this.tabPanel.stripWrap.repaint();
37746         }
37747         this.fireEvent("activate", this.tabPanel, this);
37748     },
37749
37750     /**
37751      * Returns true if this tab is the active tab.
37752      * @return {Boolean}
37753      */
37754     isActive : function(){
37755         return this.tabPanel.getActiveTab() == this;
37756     },
37757
37758     /**
37759      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37760      */
37761     hide : function(){
37762         this.pnode.removeClass("active");
37763         this.hideAction();
37764         this.fireEvent("deactivate", this.tabPanel, this);
37765     },
37766
37767     hideAction : function(){
37768         this.bodyEl.hide();
37769         this.bodyEl.setStyle("position", "absolute");
37770         this.bodyEl.setLeft("-20000px");
37771         this.bodyEl.setTop("-20000px");
37772     },
37773
37774     showAction : function(){
37775         this.bodyEl.setStyle("position", "relative");
37776         this.bodyEl.setTop("");
37777         this.bodyEl.setLeft("");
37778         this.bodyEl.show();
37779     },
37780
37781     /**
37782      * Set the tooltip for the tab.
37783      * @param {String} tooltip The tab's tooltip
37784      */
37785     setTooltip : function(text){
37786         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37787             this.textEl.dom.qtip = text;
37788             this.textEl.dom.removeAttribute('title');
37789         }else{
37790             this.textEl.dom.title = text;
37791         }
37792     },
37793
37794     onTabClick : function(e){
37795         e.preventDefault();
37796         this.tabPanel.activate(this.id);
37797     },
37798
37799     onTabMouseDown : function(e){
37800         e.preventDefault();
37801         this.tabPanel.activate(this.id);
37802     },
37803 /*
37804     getWidth : function(){
37805         return this.inner.getWidth();
37806     },
37807
37808     setWidth : function(width){
37809         var iwidth = width - this.pnode.getPadding("lr");
37810         this.inner.setWidth(iwidth);
37811         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37812         this.pnode.setWidth(width);
37813     },
37814 */
37815     /**
37816      * Show or hide the tab
37817      * @param {Boolean} hidden True to hide or false to show.
37818      */
37819     setHidden : function(hidden){
37820         this.hidden = hidden;
37821         this.pnode.setStyle("display", hidden ? "none" : "");
37822     },
37823
37824     /**
37825      * Returns true if this tab is "hidden"
37826      * @return {Boolean}
37827      */
37828     isHidden : function(){
37829         return this.hidden;
37830     },
37831
37832     /**
37833      * Returns the text for this tab
37834      * @return {String}
37835      */
37836     getText : function(){
37837         return this.text;
37838     },
37839     /*
37840     autoSize : function(){
37841         //this.el.beginMeasure();
37842         this.textEl.setWidth(1);
37843         /*
37844          *  #2804 [new] Tabs in Roojs
37845          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37846          */
37847         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37848         //this.el.endMeasure();
37849     //},
37850
37851     /**
37852      * Sets the text for the tab (Note: this also sets the tooltip text)
37853      * @param {String} text The tab's text and tooltip
37854      */
37855     setText : function(text){
37856         this.text = text;
37857         this.textEl.update(text);
37858         this.setTooltip(text);
37859         //if(!this.tabPanel.resizeTabs){
37860         //    this.autoSize();
37861         //}
37862     },
37863     /**
37864      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37865      */
37866     activate : function(){
37867         this.tabPanel.activate(this.id);
37868     },
37869
37870     /**
37871      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37872      */
37873     disable : function(){
37874         if(this.tabPanel.active != this){
37875             this.disabled = true;
37876             this.pnode.addClass("disabled");
37877         }
37878     },
37879
37880     /**
37881      * Enables this TabPanelItem if it was previously disabled.
37882      */
37883     enable : function(){
37884         this.disabled = false;
37885         this.pnode.removeClass("disabled");
37886     },
37887
37888     /**
37889      * Sets the content for this TabPanelItem.
37890      * @param {String} content The content
37891      * @param {Boolean} loadScripts true to look for and load scripts
37892      */
37893     setContent : function(content, loadScripts){
37894         this.bodyEl.update(content, loadScripts);
37895     },
37896
37897     /**
37898      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37899      * @return {Roo.UpdateManager} The UpdateManager
37900      */
37901     getUpdateManager : function(){
37902         return this.bodyEl.getUpdateManager();
37903     },
37904
37905     /**
37906      * Set a URL to be used to load the content for this TabPanelItem.
37907      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37908      * @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)
37909      * @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)
37910      * @return {Roo.UpdateManager} The UpdateManager
37911      */
37912     setUrl : function(url, params, loadOnce){
37913         if(this.refreshDelegate){
37914             this.un('activate', this.refreshDelegate);
37915         }
37916         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37917         this.on("activate", this.refreshDelegate);
37918         return this.bodyEl.getUpdateManager();
37919     },
37920
37921     /** @private */
37922     _handleRefresh : function(url, params, loadOnce){
37923         if(!loadOnce || !this.loaded){
37924             var updater = this.bodyEl.getUpdateManager();
37925             updater.update(url, params, this._setLoaded.createDelegate(this));
37926         }
37927     },
37928
37929     /**
37930      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37931      *   Will fail silently if the setUrl method has not been called.
37932      *   This does not activate the panel, just updates its content.
37933      */
37934     refresh : function(){
37935         if(this.refreshDelegate){
37936            this.loaded = false;
37937            this.refreshDelegate();
37938         }
37939     },
37940
37941     /** @private */
37942     _setLoaded : function(){
37943         this.loaded = true;
37944     },
37945
37946     /** @private */
37947     closeClick : function(e){
37948         var o = {};
37949         e.stopEvent();
37950         this.fireEvent("beforeclose", this, o);
37951         if(o.cancel !== true){
37952             this.tabPanel.removeTab(this.id);
37953         }
37954     },
37955     /**
37956      * The text displayed in the tooltip for the close icon.
37957      * @type String
37958      */
37959     closeText : "Close this tab"
37960 });
37961 /**
37962 *    This script refer to:
37963 *    Title: International Telephone Input
37964 *    Author: Jack O'Connor
37965 *    Code version:  v12.1.12
37966 *    Availability: https://github.com/jackocnr/intl-tel-input.git
37967 **/
37968
37969 Roo.bootstrap.PhoneInputData = function() {
37970     var d = [
37971       [
37972         "Afghanistan (‫افغانستان‬‎)",
37973         "af",
37974         "93"
37975       ],
37976       [
37977         "Albania (Shqipëri)",
37978         "al",
37979         "355"
37980       ],
37981       [
37982         "Algeria (‫الجزائر‬‎)",
37983         "dz",
37984         "213"
37985       ],
37986       [
37987         "American Samoa",
37988         "as",
37989         "1684"
37990       ],
37991       [
37992         "Andorra",
37993         "ad",
37994         "376"
37995       ],
37996       [
37997         "Angola",
37998         "ao",
37999         "244"
38000       ],
38001       [
38002         "Anguilla",
38003         "ai",
38004         "1264"
38005       ],
38006       [
38007         "Antigua and Barbuda",
38008         "ag",
38009         "1268"
38010       ],
38011       [
38012         "Argentina",
38013         "ar",
38014         "54"
38015       ],
38016       [
38017         "Armenia (Հայաստան)",
38018         "am",
38019         "374"
38020       ],
38021       [
38022         "Aruba",
38023         "aw",
38024         "297"
38025       ],
38026       [
38027         "Australia",
38028         "au",
38029         "61",
38030         0
38031       ],
38032       [
38033         "Austria (Österreich)",
38034         "at",
38035         "43"
38036       ],
38037       [
38038         "Azerbaijan (Azərbaycan)",
38039         "az",
38040         "994"
38041       ],
38042       [
38043         "Bahamas",
38044         "bs",
38045         "1242"
38046       ],
38047       [
38048         "Bahrain (‫البحرين‬‎)",
38049         "bh",
38050         "973"
38051       ],
38052       [
38053         "Bangladesh (বাংলাদেশ)",
38054         "bd",
38055         "880"
38056       ],
38057       [
38058         "Barbados",
38059         "bb",
38060         "1246"
38061       ],
38062       [
38063         "Belarus (Беларусь)",
38064         "by",
38065         "375"
38066       ],
38067       [
38068         "Belgium (België)",
38069         "be",
38070         "32"
38071       ],
38072       [
38073         "Belize",
38074         "bz",
38075         "501"
38076       ],
38077       [
38078         "Benin (Bénin)",
38079         "bj",
38080         "229"
38081       ],
38082       [
38083         "Bermuda",
38084         "bm",
38085         "1441"
38086       ],
38087       [
38088         "Bhutan (འབྲུག)",
38089         "bt",
38090         "975"
38091       ],
38092       [
38093         "Bolivia",
38094         "bo",
38095         "591"
38096       ],
38097       [
38098         "Bosnia and Herzegovina (Босна и Херцеговина)",
38099         "ba",
38100         "387"
38101       ],
38102       [
38103         "Botswana",
38104         "bw",
38105         "267"
38106       ],
38107       [
38108         "Brazil (Brasil)",
38109         "br",
38110         "55"
38111       ],
38112       [
38113         "British Indian Ocean Territory",
38114         "io",
38115         "246"
38116       ],
38117       [
38118         "British Virgin Islands",
38119         "vg",
38120         "1284"
38121       ],
38122       [
38123         "Brunei",
38124         "bn",
38125         "673"
38126       ],
38127       [
38128         "Bulgaria (България)",
38129         "bg",
38130         "359"
38131       ],
38132       [
38133         "Burkina Faso",
38134         "bf",
38135         "226"
38136       ],
38137       [
38138         "Burundi (Uburundi)",
38139         "bi",
38140         "257"
38141       ],
38142       [
38143         "Cambodia (កម្ពុជា)",
38144         "kh",
38145         "855"
38146       ],
38147       [
38148         "Cameroon (Cameroun)",
38149         "cm",
38150         "237"
38151       ],
38152       [
38153         "Canada",
38154         "ca",
38155         "1",
38156         1,
38157         ["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"]
38158       ],
38159       [
38160         "Cape Verde (Kabu Verdi)",
38161         "cv",
38162         "238"
38163       ],
38164       [
38165         "Caribbean Netherlands",
38166         "bq",
38167         "599",
38168         1
38169       ],
38170       [
38171         "Cayman Islands",
38172         "ky",
38173         "1345"
38174       ],
38175       [
38176         "Central African Republic (République centrafricaine)",
38177         "cf",
38178         "236"
38179       ],
38180       [
38181         "Chad (Tchad)",
38182         "td",
38183         "235"
38184       ],
38185       [
38186         "Chile",
38187         "cl",
38188         "56"
38189       ],
38190       [
38191         "China (中国)",
38192         "cn",
38193         "86"
38194       ],
38195       [
38196         "Christmas Island",
38197         "cx",
38198         "61",
38199         2
38200       ],
38201       [
38202         "Cocos (Keeling) Islands",
38203         "cc",
38204         "61",
38205         1
38206       ],
38207       [
38208         "Colombia",
38209         "co",
38210         "57"
38211       ],
38212       [
38213         "Comoros (‫جزر القمر‬‎)",
38214         "km",
38215         "269"
38216       ],
38217       [
38218         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38219         "cd",
38220         "243"
38221       ],
38222       [
38223         "Congo (Republic) (Congo-Brazzaville)",
38224         "cg",
38225         "242"
38226       ],
38227       [
38228         "Cook Islands",
38229         "ck",
38230         "682"
38231       ],
38232       [
38233         "Costa Rica",
38234         "cr",
38235         "506"
38236       ],
38237       [
38238         "Côte d’Ivoire",
38239         "ci",
38240         "225"
38241       ],
38242       [
38243         "Croatia (Hrvatska)",
38244         "hr",
38245         "385"
38246       ],
38247       [
38248         "Cuba",
38249         "cu",
38250         "53"
38251       ],
38252       [
38253         "Curaçao",
38254         "cw",
38255         "599",
38256         0
38257       ],
38258       [
38259         "Cyprus (Κύπρος)",
38260         "cy",
38261         "357"
38262       ],
38263       [
38264         "Czech Republic (Česká republika)",
38265         "cz",
38266         "420"
38267       ],
38268       [
38269         "Denmark (Danmark)",
38270         "dk",
38271         "45"
38272       ],
38273       [
38274         "Djibouti",
38275         "dj",
38276         "253"
38277       ],
38278       [
38279         "Dominica",
38280         "dm",
38281         "1767"
38282       ],
38283       [
38284         "Dominican Republic (República Dominicana)",
38285         "do",
38286         "1",
38287         2,
38288         ["809", "829", "849"]
38289       ],
38290       [
38291         "Ecuador",
38292         "ec",
38293         "593"
38294       ],
38295       [
38296         "Egypt (‫مصر‬‎)",
38297         "eg",
38298         "20"
38299       ],
38300       [
38301         "El Salvador",
38302         "sv",
38303         "503"
38304       ],
38305       [
38306         "Equatorial Guinea (Guinea Ecuatorial)",
38307         "gq",
38308         "240"
38309       ],
38310       [
38311         "Eritrea",
38312         "er",
38313         "291"
38314       ],
38315       [
38316         "Estonia (Eesti)",
38317         "ee",
38318         "372"
38319       ],
38320       [
38321         "Ethiopia",
38322         "et",
38323         "251"
38324       ],
38325       [
38326         "Falkland Islands (Islas Malvinas)",
38327         "fk",
38328         "500"
38329       ],
38330       [
38331         "Faroe Islands (Føroyar)",
38332         "fo",
38333         "298"
38334       ],
38335       [
38336         "Fiji",
38337         "fj",
38338         "679"
38339       ],
38340       [
38341         "Finland (Suomi)",
38342         "fi",
38343         "358",
38344         0
38345       ],
38346       [
38347         "France",
38348         "fr",
38349         "33"
38350       ],
38351       [
38352         "French Guiana (Guyane française)",
38353         "gf",
38354         "594"
38355       ],
38356       [
38357         "French Polynesia (Polynésie française)",
38358         "pf",
38359         "689"
38360       ],
38361       [
38362         "Gabon",
38363         "ga",
38364         "241"
38365       ],
38366       [
38367         "Gambia",
38368         "gm",
38369         "220"
38370       ],
38371       [
38372         "Georgia (საქართველო)",
38373         "ge",
38374         "995"
38375       ],
38376       [
38377         "Germany (Deutschland)",
38378         "de",
38379         "49"
38380       ],
38381       [
38382         "Ghana (Gaana)",
38383         "gh",
38384         "233"
38385       ],
38386       [
38387         "Gibraltar",
38388         "gi",
38389         "350"
38390       ],
38391       [
38392         "Greece (Ελλάδα)",
38393         "gr",
38394         "30"
38395       ],
38396       [
38397         "Greenland (Kalaallit Nunaat)",
38398         "gl",
38399         "299"
38400       ],
38401       [
38402         "Grenada",
38403         "gd",
38404         "1473"
38405       ],
38406       [
38407         "Guadeloupe",
38408         "gp",
38409         "590",
38410         0
38411       ],
38412       [
38413         "Guam",
38414         "gu",
38415         "1671"
38416       ],
38417       [
38418         "Guatemala",
38419         "gt",
38420         "502"
38421       ],
38422       [
38423         "Guernsey",
38424         "gg",
38425         "44",
38426         1
38427       ],
38428       [
38429         "Guinea (Guinée)",
38430         "gn",
38431         "224"
38432       ],
38433       [
38434         "Guinea-Bissau (Guiné Bissau)",
38435         "gw",
38436         "245"
38437       ],
38438       [
38439         "Guyana",
38440         "gy",
38441         "592"
38442       ],
38443       [
38444         "Haiti",
38445         "ht",
38446         "509"
38447       ],
38448       [
38449         "Honduras",
38450         "hn",
38451         "504"
38452       ],
38453       [
38454         "Hong Kong (香港)",
38455         "hk",
38456         "852"
38457       ],
38458       [
38459         "Hungary (Magyarország)",
38460         "hu",
38461         "36"
38462       ],
38463       [
38464         "Iceland (Ísland)",
38465         "is",
38466         "354"
38467       ],
38468       [
38469         "India (भारत)",
38470         "in",
38471         "91"
38472       ],
38473       [
38474         "Indonesia",
38475         "id",
38476         "62"
38477       ],
38478       [
38479         "Iran (‫ایران‬‎)",
38480         "ir",
38481         "98"
38482       ],
38483       [
38484         "Iraq (‫العراق‬‎)",
38485         "iq",
38486         "964"
38487       ],
38488       [
38489         "Ireland",
38490         "ie",
38491         "353"
38492       ],
38493       [
38494         "Isle of Man",
38495         "im",
38496         "44",
38497         2
38498       ],
38499       [
38500         "Israel (‫ישראל‬‎)",
38501         "il",
38502         "972"
38503       ],
38504       [
38505         "Italy (Italia)",
38506         "it",
38507         "39",
38508         0
38509       ],
38510       [
38511         "Jamaica",
38512         "jm",
38513         "1876"
38514       ],
38515       [
38516         "Japan (日本)",
38517         "jp",
38518         "81"
38519       ],
38520       [
38521         "Jersey",
38522         "je",
38523         "44",
38524         3
38525       ],
38526       [
38527         "Jordan (‫الأردن‬‎)",
38528         "jo",
38529         "962"
38530       ],
38531       [
38532         "Kazakhstan (Казахстан)",
38533         "kz",
38534         "7",
38535         1
38536       ],
38537       [
38538         "Kenya",
38539         "ke",
38540         "254"
38541       ],
38542       [
38543         "Kiribati",
38544         "ki",
38545         "686"
38546       ],
38547       [
38548         "Kosovo",
38549         "xk",
38550         "383"
38551       ],
38552       [
38553         "Kuwait (‫الكويت‬‎)",
38554         "kw",
38555         "965"
38556       ],
38557       [
38558         "Kyrgyzstan (Кыргызстан)",
38559         "kg",
38560         "996"
38561       ],
38562       [
38563         "Laos (ລາວ)",
38564         "la",
38565         "856"
38566       ],
38567       [
38568         "Latvia (Latvija)",
38569         "lv",
38570         "371"
38571       ],
38572       [
38573         "Lebanon (‫لبنان‬‎)",
38574         "lb",
38575         "961"
38576       ],
38577       [
38578         "Lesotho",
38579         "ls",
38580         "266"
38581       ],
38582       [
38583         "Liberia",
38584         "lr",
38585         "231"
38586       ],
38587       [
38588         "Libya (‫ليبيا‬‎)",
38589         "ly",
38590         "218"
38591       ],
38592       [
38593         "Liechtenstein",
38594         "li",
38595         "423"
38596       ],
38597       [
38598         "Lithuania (Lietuva)",
38599         "lt",
38600         "370"
38601       ],
38602       [
38603         "Luxembourg",
38604         "lu",
38605         "352"
38606       ],
38607       [
38608         "Macau (澳門)",
38609         "mo",
38610         "853"
38611       ],
38612       [
38613         "Macedonia (FYROM) (Македонија)",
38614         "mk",
38615         "389"
38616       ],
38617       [
38618         "Madagascar (Madagasikara)",
38619         "mg",
38620         "261"
38621       ],
38622       [
38623         "Malawi",
38624         "mw",
38625         "265"
38626       ],
38627       [
38628         "Malaysia",
38629         "my",
38630         "60"
38631       ],
38632       [
38633         "Maldives",
38634         "mv",
38635         "960"
38636       ],
38637       [
38638         "Mali",
38639         "ml",
38640         "223"
38641       ],
38642       [
38643         "Malta",
38644         "mt",
38645         "356"
38646       ],
38647       [
38648         "Marshall Islands",
38649         "mh",
38650         "692"
38651       ],
38652       [
38653         "Martinique",
38654         "mq",
38655         "596"
38656       ],
38657       [
38658         "Mauritania (‫موريتانيا‬‎)",
38659         "mr",
38660         "222"
38661       ],
38662       [
38663         "Mauritius (Moris)",
38664         "mu",
38665         "230"
38666       ],
38667       [
38668         "Mayotte",
38669         "yt",
38670         "262",
38671         1
38672       ],
38673       [
38674         "Mexico (México)",
38675         "mx",
38676         "52"
38677       ],
38678       [
38679         "Micronesia",
38680         "fm",
38681         "691"
38682       ],
38683       [
38684         "Moldova (Republica Moldova)",
38685         "md",
38686         "373"
38687       ],
38688       [
38689         "Monaco",
38690         "mc",
38691         "377"
38692       ],
38693       [
38694         "Mongolia (Монгол)",
38695         "mn",
38696         "976"
38697       ],
38698       [
38699         "Montenegro (Crna Gora)",
38700         "me",
38701         "382"
38702       ],
38703       [
38704         "Montserrat",
38705         "ms",
38706         "1664"
38707       ],
38708       [
38709         "Morocco (‫المغرب‬‎)",
38710         "ma",
38711         "212",
38712         0
38713       ],
38714       [
38715         "Mozambique (Moçambique)",
38716         "mz",
38717         "258"
38718       ],
38719       [
38720         "Myanmar (Burma) (မြန်မာ)",
38721         "mm",
38722         "95"
38723       ],
38724       [
38725         "Namibia (Namibië)",
38726         "na",
38727         "264"
38728       ],
38729       [
38730         "Nauru",
38731         "nr",
38732         "674"
38733       ],
38734       [
38735         "Nepal (नेपाल)",
38736         "np",
38737         "977"
38738       ],
38739       [
38740         "Netherlands (Nederland)",
38741         "nl",
38742         "31"
38743       ],
38744       [
38745         "New Caledonia (Nouvelle-Calédonie)",
38746         "nc",
38747         "687"
38748       ],
38749       [
38750         "New Zealand",
38751         "nz",
38752         "64"
38753       ],
38754       [
38755         "Nicaragua",
38756         "ni",
38757         "505"
38758       ],
38759       [
38760         "Niger (Nijar)",
38761         "ne",
38762         "227"
38763       ],
38764       [
38765         "Nigeria",
38766         "ng",
38767         "234"
38768       ],
38769       [
38770         "Niue",
38771         "nu",
38772         "683"
38773       ],
38774       [
38775         "Norfolk Island",
38776         "nf",
38777         "672"
38778       ],
38779       [
38780         "North Korea (조선 민주주의 인민 공화국)",
38781         "kp",
38782         "850"
38783       ],
38784       [
38785         "Northern Mariana Islands",
38786         "mp",
38787         "1670"
38788       ],
38789       [
38790         "Norway (Norge)",
38791         "no",
38792         "47",
38793         0
38794       ],
38795       [
38796         "Oman (‫عُمان‬‎)",
38797         "om",
38798         "968"
38799       ],
38800       [
38801         "Pakistan (‫پاکستان‬‎)",
38802         "pk",
38803         "92"
38804       ],
38805       [
38806         "Palau",
38807         "pw",
38808         "680"
38809       ],
38810       [
38811         "Palestine (‫فلسطين‬‎)",
38812         "ps",
38813         "970"
38814       ],
38815       [
38816         "Panama (Panamá)",
38817         "pa",
38818         "507"
38819       ],
38820       [
38821         "Papua New Guinea",
38822         "pg",
38823         "675"
38824       ],
38825       [
38826         "Paraguay",
38827         "py",
38828         "595"
38829       ],
38830       [
38831         "Peru (Perú)",
38832         "pe",
38833         "51"
38834       ],
38835       [
38836         "Philippines",
38837         "ph",
38838         "63"
38839       ],
38840       [
38841         "Poland (Polska)",
38842         "pl",
38843         "48"
38844       ],
38845       [
38846         "Portugal",
38847         "pt",
38848         "351"
38849       ],
38850       [
38851         "Puerto Rico",
38852         "pr",
38853         "1",
38854         3,
38855         ["787", "939"]
38856       ],
38857       [
38858         "Qatar (‫قطر‬‎)",
38859         "qa",
38860         "974"
38861       ],
38862       [
38863         "Réunion (La Réunion)",
38864         "re",
38865         "262",
38866         0
38867       ],
38868       [
38869         "Romania (România)",
38870         "ro",
38871         "40"
38872       ],
38873       [
38874         "Russia (Россия)",
38875         "ru",
38876         "7",
38877         0
38878       ],
38879       [
38880         "Rwanda",
38881         "rw",
38882         "250"
38883       ],
38884       [
38885         "Saint Barthélemy",
38886         "bl",
38887         "590",
38888         1
38889       ],
38890       [
38891         "Saint Helena",
38892         "sh",
38893         "290"
38894       ],
38895       [
38896         "Saint Kitts and Nevis",
38897         "kn",
38898         "1869"
38899       ],
38900       [
38901         "Saint Lucia",
38902         "lc",
38903         "1758"
38904       ],
38905       [
38906         "Saint Martin (Saint-Martin (partie française))",
38907         "mf",
38908         "590",
38909         2
38910       ],
38911       [
38912         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38913         "pm",
38914         "508"
38915       ],
38916       [
38917         "Saint Vincent and the Grenadines",
38918         "vc",
38919         "1784"
38920       ],
38921       [
38922         "Samoa",
38923         "ws",
38924         "685"
38925       ],
38926       [
38927         "San Marino",
38928         "sm",
38929         "378"
38930       ],
38931       [
38932         "São Tomé and Príncipe (São Tomé e Príncipe)",
38933         "st",
38934         "239"
38935       ],
38936       [
38937         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
38938         "sa",
38939         "966"
38940       ],
38941       [
38942         "Senegal (Sénégal)",
38943         "sn",
38944         "221"
38945       ],
38946       [
38947         "Serbia (Србија)",
38948         "rs",
38949         "381"
38950       ],
38951       [
38952         "Seychelles",
38953         "sc",
38954         "248"
38955       ],
38956       [
38957         "Sierra Leone",
38958         "sl",
38959         "232"
38960       ],
38961       [
38962         "Singapore",
38963         "sg",
38964         "65"
38965       ],
38966       [
38967         "Sint Maarten",
38968         "sx",
38969         "1721"
38970       ],
38971       [
38972         "Slovakia (Slovensko)",
38973         "sk",
38974         "421"
38975       ],
38976       [
38977         "Slovenia (Slovenija)",
38978         "si",
38979         "386"
38980       ],
38981       [
38982         "Solomon Islands",
38983         "sb",
38984         "677"
38985       ],
38986       [
38987         "Somalia (Soomaaliya)",
38988         "so",
38989         "252"
38990       ],
38991       [
38992         "South Africa",
38993         "za",
38994         "27"
38995       ],
38996       [
38997         "South Korea (대한민국)",
38998         "kr",
38999         "82"
39000       ],
39001       [
39002         "South Sudan (‫جنوب السودان‬‎)",
39003         "ss",
39004         "211"
39005       ],
39006       [
39007         "Spain (España)",
39008         "es",
39009         "34"
39010       ],
39011       [
39012         "Sri Lanka (ශ්‍රී ලංකාව)",
39013         "lk",
39014         "94"
39015       ],
39016       [
39017         "Sudan (‫السودان‬‎)",
39018         "sd",
39019         "249"
39020       ],
39021       [
39022         "Suriname",
39023         "sr",
39024         "597"
39025       ],
39026       [
39027         "Svalbard and Jan Mayen",
39028         "sj",
39029         "47",
39030         1
39031       ],
39032       [
39033         "Swaziland",
39034         "sz",
39035         "268"
39036       ],
39037       [
39038         "Sweden (Sverige)",
39039         "se",
39040         "46"
39041       ],
39042       [
39043         "Switzerland (Schweiz)",
39044         "ch",
39045         "41"
39046       ],
39047       [
39048         "Syria (‫سوريا‬‎)",
39049         "sy",
39050         "963"
39051       ],
39052       [
39053         "Taiwan (台灣)",
39054         "tw",
39055         "886"
39056       ],
39057       [
39058         "Tajikistan",
39059         "tj",
39060         "992"
39061       ],
39062       [
39063         "Tanzania",
39064         "tz",
39065         "255"
39066       ],
39067       [
39068         "Thailand (ไทย)",
39069         "th",
39070         "66"
39071       ],
39072       [
39073         "Timor-Leste",
39074         "tl",
39075         "670"
39076       ],
39077       [
39078         "Togo",
39079         "tg",
39080         "228"
39081       ],
39082       [
39083         "Tokelau",
39084         "tk",
39085         "690"
39086       ],
39087       [
39088         "Tonga",
39089         "to",
39090         "676"
39091       ],
39092       [
39093         "Trinidad and Tobago",
39094         "tt",
39095         "1868"
39096       ],
39097       [
39098         "Tunisia (‫تونس‬‎)",
39099         "tn",
39100         "216"
39101       ],
39102       [
39103         "Turkey (Türkiye)",
39104         "tr",
39105         "90"
39106       ],
39107       [
39108         "Turkmenistan",
39109         "tm",
39110         "993"
39111       ],
39112       [
39113         "Turks and Caicos Islands",
39114         "tc",
39115         "1649"
39116       ],
39117       [
39118         "Tuvalu",
39119         "tv",
39120         "688"
39121       ],
39122       [
39123         "U.S. Virgin Islands",
39124         "vi",
39125         "1340"
39126       ],
39127       [
39128         "Uganda",
39129         "ug",
39130         "256"
39131       ],
39132       [
39133         "Ukraine (Україна)",
39134         "ua",
39135         "380"
39136       ],
39137       [
39138         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39139         "ae",
39140         "971"
39141       ],
39142       [
39143         "United Kingdom",
39144         "gb",
39145         "44",
39146         0
39147       ],
39148       [
39149         "United States",
39150         "us",
39151         "1",
39152         0
39153       ],
39154       [
39155         "Uruguay",
39156         "uy",
39157         "598"
39158       ],
39159       [
39160         "Uzbekistan (Oʻzbekiston)",
39161         "uz",
39162         "998"
39163       ],
39164       [
39165         "Vanuatu",
39166         "vu",
39167         "678"
39168       ],
39169       [
39170         "Vatican City (Città del Vaticano)",
39171         "va",
39172         "39",
39173         1
39174       ],
39175       [
39176         "Venezuela",
39177         "ve",
39178         "58"
39179       ],
39180       [
39181         "Vietnam (Việt Nam)",
39182         "vn",
39183         "84"
39184       ],
39185       [
39186         "Wallis and Futuna (Wallis-et-Futuna)",
39187         "wf",
39188         "681"
39189       ],
39190       [
39191         "Western Sahara (‫الصحراء الغربية‬‎)",
39192         "eh",
39193         "212",
39194         1
39195       ],
39196       [
39197         "Yemen (‫اليمن‬‎)",
39198         "ye",
39199         "967"
39200       ],
39201       [
39202         "Zambia",
39203         "zm",
39204         "260"
39205       ],
39206       [
39207         "Zimbabwe",
39208         "zw",
39209         "263"
39210       ],
39211       [
39212         "Åland Islands",
39213         "ax",
39214         "358",
39215         1
39216       ]
39217   ];
39218   
39219   return d;
39220 }/**
39221 *    This script refer to:
39222 *    Title: International Telephone Input
39223 *    Author: Jack O'Connor
39224 *    Code version:  v12.1.12
39225 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39226 **/
39227
39228 /**
39229  * @class Roo.bootstrap.PhoneInput
39230  * @extends Roo.bootstrap.TriggerField
39231  * An input with International dial-code selection
39232  
39233  * @cfg {String} defaultDialCode default '+852'
39234  * @cfg {Array} preferedCountries default []
39235   
39236  * @constructor
39237  * Create a new PhoneInput.
39238  * @param {Object} config Configuration options
39239  */
39240
39241 Roo.bootstrap.PhoneInput = function(config) {
39242     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39243 };
39244
39245 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39246         
39247         listWidth: undefined,
39248         
39249         selectedClass: 'active',
39250         
39251         invalidClass : "has-warning",
39252         
39253         validClass: 'has-success',
39254         
39255         allowed: '0123456789',
39256         
39257         /**
39258          * @cfg {String} defaultDialCode The default dial code when initializing the input
39259          */
39260         defaultDialCode: '+852',
39261         
39262         /**
39263          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39264          */
39265         preferedCountries: false,
39266         
39267         getAutoCreate : function()
39268         {
39269             var data = Roo.bootstrap.PhoneInputData();
39270             var align = this.labelAlign || this.parentLabelAlign();
39271             var id = Roo.id();
39272             
39273             this.allCountries = [];
39274             this.dialCodeMapping = [];
39275             
39276             for (var i = 0; i < data.length; i++) {
39277               var c = data[i];
39278               this.allCountries[i] = {
39279                 name: c[0],
39280                 iso2: c[1],
39281                 dialCode: c[2],
39282                 priority: c[3] || 0,
39283                 areaCodes: c[4] || null
39284               };
39285               this.dialCodeMapping[c[2]] = {
39286                   name: c[0],
39287                   iso2: c[1],
39288                   priority: c[3] || 0,
39289                   areaCodes: c[4] || null
39290               };
39291             }
39292             
39293             var cfg = {
39294                 cls: 'form-group',
39295                 cn: []
39296             };
39297             
39298             var input =  {
39299                 tag: 'input',
39300                 id : id,
39301                 cls : 'form-control tel-input',
39302                 autocomplete: 'new-password'
39303             };
39304             
39305             var hiddenInput = {
39306                 tag: 'input',
39307                 type: 'hidden',
39308                 cls: 'hidden-tel-input'
39309             };
39310             
39311             if (this.name) {
39312                 hiddenInput.name = this.name;
39313             }
39314             
39315             if (this.disabled) {
39316                 input.disabled = true;
39317             }
39318             
39319             var flag_container = {
39320                 tag: 'div',
39321                 cls: 'flag-box',
39322                 cn: [
39323                     {
39324                         tag: 'div',
39325                         cls: 'flag'
39326                     },
39327                     {
39328                         tag: 'div',
39329                         cls: 'caret'
39330                     }
39331                 ]
39332             };
39333             
39334             var box = {
39335                 tag: 'div',
39336                 cls: this.hasFeedback ? 'has-feedback' : '',
39337                 cn: [
39338                     hiddenInput,
39339                     input,
39340                     {
39341                         tag: 'input',
39342                         cls: 'dial-code-holder',
39343                         disabled: true
39344                     }
39345                 ]
39346             };
39347             
39348             var container = {
39349                 cls: 'roo-select2-container input-group',
39350                 cn: [
39351                     flag_container,
39352                     box
39353                 ]
39354             };
39355             
39356             if (this.fieldLabel.length) {
39357                 var indicator = {
39358                     tag: 'i',
39359                     tooltip: 'This field is required'
39360                 };
39361                 
39362                 var label = {
39363                     tag: 'label',
39364                     'for':  id,
39365                     cls: 'control-label',
39366                     cn: []
39367                 };
39368                 
39369                 var label_text = {
39370                     tag: 'span',
39371                     html: this.fieldLabel
39372                 };
39373                 
39374                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39375                 label.cn = [
39376                     indicator,
39377                     label_text
39378                 ];
39379                 
39380                 if(this.indicatorpos == 'right') {
39381                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39382                     label.cn = [
39383                         label_text,
39384                         indicator
39385                     ];
39386                 }
39387                 
39388                 if(align == 'left') {
39389                     container = {
39390                         tag: 'div',
39391                         cn: [
39392                             container
39393                         ]
39394                     };
39395                     
39396                     if(this.labelWidth > 12){
39397                         label.style = "width: " + this.labelWidth + 'px';
39398                     }
39399                     if(this.labelWidth < 13 && this.labelmd == 0){
39400                         this.labelmd = this.labelWidth;
39401                     }
39402                     if(this.labellg > 0){
39403                         label.cls += ' col-lg-' + this.labellg;
39404                         input.cls += ' col-lg-' + (12 - this.labellg);
39405                     }
39406                     if(this.labelmd > 0){
39407                         label.cls += ' col-md-' + this.labelmd;
39408                         container.cls += ' col-md-' + (12 - this.labelmd);
39409                     }
39410                     if(this.labelsm > 0){
39411                         label.cls += ' col-sm-' + this.labelsm;
39412                         container.cls += ' col-sm-' + (12 - this.labelsm);
39413                     }
39414                     if(this.labelxs > 0){
39415                         label.cls += ' col-xs-' + this.labelxs;
39416                         container.cls += ' col-xs-' + (12 - this.labelxs);
39417                     }
39418                 }
39419             }
39420             
39421             cfg.cn = [
39422                 label,
39423                 container
39424             ];
39425             
39426             var settings = this;
39427             
39428             ['xs','sm','md','lg'].map(function(size){
39429                 if (settings[size]) {
39430                     cfg.cls += ' col-' + size + '-' + settings[size];
39431                 }
39432             });
39433             
39434             this.store = new Roo.data.Store({
39435                 proxy : new Roo.data.MemoryProxy({}),
39436                 reader : new Roo.data.JsonReader({
39437                     fields : [
39438                         {
39439                             'name' : 'name',
39440                             'type' : 'string'
39441                         },
39442                         {
39443                             'name' : 'iso2',
39444                             'type' : 'string'
39445                         },
39446                         {
39447                             'name' : 'dialCode',
39448                             'type' : 'string'
39449                         },
39450                         {
39451                             'name' : 'priority',
39452                             'type' : 'string'
39453                         },
39454                         {
39455                             'name' : 'areaCodes',
39456                             'type' : 'string'
39457                         }
39458                     ]
39459                 })
39460             });
39461             
39462             if(!this.preferedCountries) {
39463                 this.preferedCountries = [
39464                     'hk',
39465                     'gb',
39466                     'us'
39467                 ];
39468             }
39469             
39470             var p = this.preferedCountries.reverse();
39471             
39472             if(p) {
39473                 for (var i = 0; i < p.length; i++) {
39474                     for (var j = 0; j < this.allCountries.length; j++) {
39475                         if(this.allCountries[j].iso2 == p[i]) {
39476                             var t = this.allCountries[j];
39477                             this.allCountries.splice(j,1);
39478                             this.allCountries.unshift(t);
39479                         }
39480                     } 
39481                 }
39482             }
39483             
39484             this.store.proxy.data = {
39485                 success: true,
39486                 data: this.allCountries
39487             };
39488             
39489             return cfg;
39490         },
39491         
39492         initEvents : function()
39493         {
39494             this.createList();
39495             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39496             
39497             this.indicator = this.indicatorEl();
39498             this.flag = this.flagEl();
39499             this.dialCodeHolder = this.dialCodeHolderEl();
39500             
39501             this.trigger = this.el.select('div.flag-box',true).first();
39502             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39503             
39504             var _this = this;
39505             
39506             (function(){
39507                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39508                 _this.list.setWidth(lw);
39509             }).defer(100);
39510             
39511             this.list.on('mouseover', this.onViewOver, this);
39512             this.list.on('mousemove', this.onViewMove, this);
39513             this.inputEl().on("keyup", this.onKeyUp, this);
39514             
39515             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39516
39517             this.view = new Roo.View(this.list, this.tpl, {
39518                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39519             });
39520             
39521             this.view.on('click', this.onViewClick, this);
39522             this.setValue(this.defaultDialCode);
39523         },
39524         
39525         onTriggerClick : function(e)
39526         {
39527             Roo.log('trigger click');
39528             if(this.disabled){
39529                 return;
39530             }
39531             
39532             if(this.isExpanded()){
39533                 this.collapse();
39534                 this.hasFocus = false;
39535             }else {
39536                 this.store.load({});
39537                 this.hasFocus = true;
39538                 this.expand();
39539             }
39540         },
39541         
39542         isExpanded : function()
39543         {
39544             return this.list.isVisible();
39545         },
39546         
39547         collapse : function()
39548         {
39549             if(!this.isExpanded()){
39550                 return;
39551             }
39552             this.list.hide();
39553             Roo.get(document).un('mousedown', this.collapseIf, this);
39554             Roo.get(document).un('mousewheel', this.collapseIf, this);
39555             this.fireEvent('collapse', this);
39556             this.validate();
39557         },
39558         
39559         expand : function()
39560         {
39561             Roo.log('expand');
39562
39563             if(this.isExpanded() || !this.hasFocus){
39564                 return;
39565             }
39566             
39567             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39568             this.list.setWidth(lw);
39569             
39570             this.list.show();
39571             this.restrictHeight();
39572             
39573             Roo.get(document).on('mousedown', this.collapseIf, this);
39574             Roo.get(document).on('mousewheel', this.collapseIf, this);
39575             
39576             this.fireEvent('expand', this);
39577         },
39578         
39579         restrictHeight : function()
39580         {
39581             this.list.alignTo(this.inputEl(), this.listAlign);
39582             this.list.alignTo(this.inputEl(), this.listAlign);
39583         },
39584         
39585         onViewOver : function(e, t)
39586         {
39587             if(this.inKeyMode){
39588                 return;
39589             }
39590             var item = this.view.findItemFromChild(t);
39591             
39592             if(item){
39593                 var index = this.view.indexOf(item);
39594                 this.select(index, false);
39595             }
39596         },
39597
39598         // private
39599         onViewClick : function(view, doFocus, el, e)
39600         {
39601             var index = this.view.getSelectedIndexes()[0];
39602             
39603             var r = this.store.getAt(index);
39604             
39605             if(r){
39606                 this.onSelect(r, index);
39607             }
39608             if(doFocus !== false && !this.blockFocus){
39609                 this.inputEl().focus();
39610             }
39611         },
39612         
39613         onViewMove : function(e, t)
39614         {
39615             this.inKeyMode = false;
39616         },
39617         
39618         select : function(index, scrollIntoView)
39619         {
39620             this.selectedIndex = index;
39621             this.view.select(index);
39622             if(scrollIntoView !== false){
39623                 var el = this.view.getNode(index);
39624                 if(el){
39625                     this.list.scrollChildIntoView(el, false);
39626                 }
39627             }
39628         },
39629         
39630         createList : function()
39631         {
39632             this.list = Roo.get(document.body).createChild({
39633                 tag: 'ul',
39634                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39635                 style: 'display:none'
39636             });
39637             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39638         },
39639         
39640         collapseIf : function(e)
39641         {
39642             var in_combo  = e.within(this.el);
39643             var in_list =  e.within(this.list);
39644             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39645             
39646             if (in_combo || in_list || is_list) {
39647                 return;
39648             }
39649             this.collapse();
39650         },
39651         
39652         onSelect : function(record, index)
39653         {
39654             if(this.fireEvent('beforeselect', this, record, index) !== false){
39655                 
39656                 this.setFlagClass(record.data.iso2);
39657                 this.setDialCode(record.data.dialCode);
39658                 this.hasFocus = false;
39659                 this.collapse();
39660                 this.fireEvent('select', this, record, index);
39661             }
39662         },
39663         
39664         flagEl : function()
39665         {
39666             var flag = this.el.select('div.flag',true).first();
39667             if(!flag){
39668                 return false;
39669             }
39670             return flag;
39671         },
39672         
39673         dialCodeHolderEl : function()
39674         {
39675             var d = this.el.select('input.dial-code-holder',true).first();
39676             if(!d){
39677                 return false;
39678             }
39679             return d;
39680         },
39681         
39682         setDialCode : function(v)
39683         {
39684             this.dialCodeHolder.dom.value = '+'+v;
39685         },
39686         
39687         setFlagClass : function(n)
39688         {
39689             this.flag.dom.className = 'flag '+n;
39690         },
39691         
39692         getValue : function()
39693         {
39694             var v = this.inputEl().getValue();
39695             if(this.dialCodeHolder) {
39696                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39697             }
39698             return v;
39699         },
39700         
39701         setValue : function(v)
39702         {
39703             var d = this.getDialCode(v);
39704             
39705             //invalid dial code
39706             if(v.length == 0 || !d || d.length == 0) {
39707                 if(this.rendered){
39708                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39709                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39710                 }
39711                 return;
39712             }
39713             
39714             //valid dial code
39715             this.setFlagClass(this.dialCodeMapping[d].iso2);
39716             this.setDialCode(d);
39717             this.inputEl().dom.value = v.replace('+'+d,'');
39718             this.hiddenEl().dom.value = this.getValue();
39719             
39720             this.validate();
39721         },
39722         
39723         getDialCode : function(v = '')
39724         {
39725             if (v.length == 0) {
39726                 return this.dialCodeHolder.dom.value;
39727             }
39728             
39729             var dialCode = "";
39730             if (v.charAt(0) != "+") {
39731                 return false;
39732             }
39733             var numericChars = "";
39734             for (var i = 1; i < v.length; i++) {
39735               var c = v.charAt(i);
39736               if (!isNaN(c)) {
39737                 numericChars += c;
39738                 if (this.dialCodeMapping[numericChars]) {
39739                   dialCode = v.substr(1, i);
39740                 }
39741                 if (numericChars.length == 4) {
39742                   break;
39743                 }
39744               }
39745             }
39746             return dialCode;
39747         },
39748         
39749         reset : function()
39750         {
39751             this.setValue(this.defaultDialCode);
39752             this.validate();
39753         },
39754         
39755         hiddenEl : function()
39756         {
39757             return this.el.select('input.hidden-tel-input',true).first();
39758         },
39759         
39760         onKeyUp : function(e){
39761             
39762             var k = e.getKey();
39763             var c = e.getCharCode();
39764             
39765             if(
39766                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39767                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39768             ){
39769                 e.stopEvent();
39770             }
39771             
39772             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39773             //     return;
39774             // }
39775             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39776                 e.stopEvent();
39777             }
39778             
39779             this.setValue(this.getValue());
39780         }
39781         
39782 });
39783 /**
39784  * @class Roo.bootstrap.MoneyField
39785  * @extends Roo.bootstrap.ComboBox
39786  * Bootstrap MoneyField class
39787  * 
39788  * @constructor
39789  * Create a new MoneyField.
39790  * @param {Object} config Configuration options
39791  */
39792
39793 Roo.bootstrap.MoneyField = function(config) {
39794     
39795     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39796     
39797 };
39798
39799 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39800     
39801     /**
39802      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39803      */
39804     allowDecimals : true,
39805     /**
39806      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39807      */
39808     decimalSeparator : ".",
39809     /**
39810      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39811      */
39812     decimalPrecision : 2,
39813     /**
39814      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39815      */
39816     allowNegative : true,
39817     /**
39818      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39819      */
39820     minValue : Number.NEGATIVE_INFINITY,
39821     /**
39822      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39823      */
39824     maxValue : Number.MAX_VALUE,
39825     /**
39826      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39827      */
39828     minText : "The minimum value for this field is {0}",
39829     /**
39830      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39831      */
39832     maxText : "The maximum value for this field is {0}",
39833     /**
39834      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39835      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39836      */
39837     nanText : "{0} is not a valid number",
39838     /**
39839      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39840      */
39841     castInt : true,
39842     
39843     inputlg : 9,
39844     inputmd : 9,
39845     inputsm : 9,
39846     inputxs : 6,
39847     
39848     store : false,
39849     
39850     getAutoCreate : function()
39851     {
39852         var align = this.labelAlign || this.parentLabelAlign();
39853         
39854         var id = Roo.id();
39855
39856         var cfg = {
39857             cls: 'form-group',
39858             cn: []
39859         };
39860
39861         var input =  {
39862             tag: 'input',
39863             id : id,
39864             cls : 'form-control roo-money-amount-input',
39865             autocomplete: 'new-password'
39866         };
39867         
39868         if (this.name) {
39869             input.name = this.name;
39870         }
39871
39872         if (this.disabled) {
39873             input.disabled = true;
39874         }
39875
39876         var clg = 12 - this.inputlg;
39877         var cmd = 12 - this.inputmd;
39878         var csm = 12 - this.inputsm;
39879         var cxs = 12 - this.inputxs;
39880         
39881         var container = {
39882             tag : 'div',
39883             cls : 'row roo-money-field',
39884             cn : [
39885                 {
39886                     tag : 'div',
39887                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39888                     cn : [
39889                         {
39890                             tag : 'div',
39891                             cls: 'roo-select2-container input-group',
39892                             cn: [
39893                                 {
39894                                     tag : 'input',
39895                                     cls : 'form-control roo-money-currency-input',
39896                                     autocomplete: 'new-password'
39897                                 },
39898                                 {
39899                                     tag :'span',
39900                                     cls : 'input-group-addon',
39901                                     cn : [
39902                                         {
39903                                             tag: 'span',
39904                                             cls: 'caret'
39905                                         }
39906                                     ]
39907                                 }
39908                             ]
39909                         }
39910                     ]
39911                 },
39912                 {
39913                     tag : 'div',
39914                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
39915                     cn : [
39916                         {
39917                             tag: 'div',
39918                             cls: this.hasFeedback ? 'has-feedback' : '',
39919                             cn: [
39920                                 input
39921                             ]
39922                         }
39923                     ]
39924                 }
39925             ]
39926             
39927         };
39928         
39929         if (this.fieldLabel.length) {
39930             var indicator = {
39931                 tag: 'i',
39932                 tooltip: 'This field is required'
39933             };
39934
39935             var label = {
39936                 tag: 'label',
39937                 'for':  id,
39938                 cls: 'control-label',
39939                 cn: []
39940             };
39941
39942             var label_text = {
39943                 tag: 'span',
39944                 html: this.fieldLabel
39945             };
39946
39947             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39948             label.cn = [
39949                 indicator,
39950                 label_text
39951             ];
39952
39953             if(this.indicatorpos == 'right') {
39954                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39955                 label.cn = [
39956                     label_text,
39957                     indicator
39958                 ];
39959             }
39960
39961             if(align == 'left') {
39962                 container = {
39963                     tag: 'div',
39964                     cn: [
39965                         container
39966                     ]
39967                 };
39968
39969                 if(this.labelWidth > 12){
39970                     label.style = "width: " + this.labelWidth + 'px';
39971                 }
39972                 if(this.labelWidth < 13 && this.labelmd == 0){
39973                     this.labelmd = this.labelWidth;
39974                 }
39975                 if(this.labellg > 0){
39976                     label.cls += ' col-lg-' + this.labellg;
39977                     input.cls += ' col-lg-' + (12 - this.labellg);
39978                 }
39979                 if(this.labelmd > 0){
39980                     label.cls += ' col-md-' + this.labelmd;
39981                     container.cls += ' col-md-' + (12 - this.labelmd);
39982                 }
39983                 if(this.labelsm > 0){
39984                     label.cls += ' col-sm-' + this.labelsm;
39985                     container.cls += ' col-sm-' + (12 - this.labelsm);
39986                 }
39987                 if(this.labelxs > 0){
39988                     label.cls += ' col-xs-' + this.labelxs;
39989                     container.cls += ' col-xs-' + (12 - this.labelxs);
39990                 }
39991             }
39992         }
39993
39994         cfg.cn = [
39995             label,
39996             container
39997         ];
39998
39999         var settings = this;
40000
40001         ['xs','sm','md','lg'].map(function(size){
40002             if (settings[size]) {
40003                 cfg.cls += ' col-' + size + '-' + settings[size];
40004             }
40005         });
40006         
40007         return cfg;
40008         
40009     },
40010     
40011     initEvents : function()
40012     {
40013         this.indicator = this.indicatorEl();
40014         
40015         this.initCurrencyEvent();
40016         
40017         this.initNumberEvent();
40018         
40019     },
40020     
40021     initCurrencyEvent : function()
40022     {
40023         if (!this.store) {
40024             throw "can not find store for combo";
40025         }
40026         
40027         this.store = Roo.factory(this.store, Roo.data);
40028         this.store.parent = this;
40029         
40030         this.createList();
40031         
40032         this.triggerEl = this.el.select('.input-group-addon', true).first();
40033         
40034         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40035         
40036         var _this = this;
40037         
40038         (function(){
40039             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40040             _this.list.setWidth(lw);
40041         }).defer(100);
40042         
40043         this.list.on('mouseover', this.onViewOver, this);
40044         this.list.on('mousemove', this.onViewMove, this);
40045         this.list.on('scroll', this.onViewScroll, this);
40046         
40047         if(!this.tpl){
40048             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40049         }
40050         
40051         this.view = new Roo.View(this.list, this.tpl, {
40052             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40053         });
40054         
40055         this.view.on('click', this.onViewClick, this);
40056         
40057         this.store.on('beforeload', this.onBeforeLoad, this);
40058         this.store.on('load', this.onLoad, this);
40059         this.store.on('loadexception', this.onLoadException, this);
40060         
40061         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40062             "up" : function(e){
40063                 this.inKeyMode = true;
40064                 this.selectPrev();
40065             },
40066
40067             "down" : function(e){
40068                 if(!this.isExpanded()){
40069                     this.onTriggerClick();
40070                 }else{
40071                     this.inKeyMode = true;
40072                     this.selectNext();
40073                 }
40074             },
40075
40076             "enter" : function(e){
40077                 this.collapse();
40078                 
40079                 if(this.fireEvent("specialkey", this, e)){
40080                     this.onViewClick(false);
40081                 }
40082                 
40083                 return true;
40084             },
40085
40086             "esc" : function(e){
40087                 this.collapse();
40088             },
40089
40090             "tab" : function(e){
40091                 this.collapse();
40092                 
40093                 if(this.fireEvent("specialkey", this, e)){
40094                     this.onViewClick(false);
40095                 }
40096                 
40097                 return true;
40098             },
40099
40100             scope : this,
40101
40102             doRelay : function(foo, bar, hname){
40103                 if(hname == 'down' || this.scope.isExpanded()){
40104                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40105                 }
40106                 return true;
40107             },
40108
40109             forceKeyDown: true
40110         });
40111         
40112         this.queryDelay = Math.max(this.queryDelay || 10,
40113                 this.mode == 'local' ? 10 : 250);
40114         
40115         
40116         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
40117         
40118         if(this.typeAhead){
40119             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
40120         }
40121         
40122         this.currencyEl().on("keyup", this.onCurrencyKeyUp, this);
40123         
40124     },
40125     
40126     initNumberEvent : function(e)
40127     {
40128         this.inputEl().on("keydown" , this.fireKey,  this);
40129         this.inputEl().on("focus", this.onFocus,  this);
40130         this.inputEl().on("blur", this.onBlur,  this);
40131         
40132         this.inputEl().relayEvent('keyup', this);
40133         
40134         if(this.indicator){
40135             this.indicator.addClass('invisible');
40136         }
40137  
40138         this.originalValue = this.getValue();
40139         
40140         if(this.validationEvent == 'keyup'){
40141             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40142             this.inputEl().on('keyup', this.filterValidation, this);
40143         }
40144         else if(this.validationEvent !== false){
40145             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40146         }
40147         
40148         if(this.selectOnFocus){
40149             this.on("focus", this.preFocus, this);
40150             
40151         }
40152         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40153             this.inputEl().on("keypress", this.filterKeys, this);
40154         } else {
40155             this.inputEl().relayEvent('keypress', this);
40156         }
40157         
40158         var allowed = "0123456789";
40159         
40160         if(this.allowDecimals){
40161             allowed += this.decimalSeparator;
40162         }
40163         
40164         if(this.allowNegative){
40165             allowed += "-";
40166         }
40167         
40168         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40169         
40170         var keyPress = function(e){
40171             
40172             var k = e.getKey();
40173             
40174             var c = e.getCharCode();
40175             
40176             if(
40177                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40178                     allowed.indexOf(String.fromCharCode(c)) === -1
40179             ){
40180                 e.stopEvent();
40181                 return;
40182             }
40183             
40184             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40185                 return;
40186             }
40187             
40188             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40189                 e.stopEvent();
40190             }
40191         };
40192         
40193         this.inputEl().on("keypress", keyPress, this);
40194         
40195     },
40196     
40197     onTriggerClick : function(e)
40198     {   
40199         if(this.disabled){
40200             return;
40201         }
40202         
40203         this.page = 0;
40204         this.loadNext = false;
40205         
40206         if(this.isExpanded()){
40207             this.collapse();
40208             return;
40209         }
40210         
40211         this.hasFocus = true;
40212         
40213         if(this.triggerAction == 'all') {
40214             this.doQuery(this.allQuery, true);
40215             return;
40216         }
40217         
40218         this.doQuery(this.getCurrency());
40219     },
40220     
40221     getCurrency : function()
40222     {   
40223         var v = this.currencyEl().getValue();
40224         
40225         return v;
40226     },
40227     
40228     restrictHeight : function()
40229     {
40230         this.list.alignTo(this.currencyEl(), this.listAlign);
40231         this.list.alignTo(this.currencyEl(), this.listAlign);
40232     },
40233     
40234     onViewClick : function(view, doFocus, el, e)
40235     {
40236         var index = this.view.getSelectedIndexes()[0];
40237         
40238         var r = this.store.getAt(index);
40239         
40240         if(r){
40241             this.onSelect(r, index);
40242         }
40243     },
40244     
40245     onSelect : function(record, index){
40246         
40247         if(this.fireEvent('beforeselect', this, record, index) !== false){
40248         
40249             this.setFromCurrencyData(index > -1 ? record.data : false);
40250             
40251             this.collapse();
40252             
40253             this.fireEvent('select', this, record, index);
40254         }
40255     },
40256     
40257     setFromCurrencyData : function(o)
40258     {
40259         var currency = '';
40260         
40261         this.lastCurrency = o;
40262         
40263         if (this.currencyField) {
40264             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40265         } else {
40266             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40267         }
40268         
40269         this.lastSelectionText = currency;
40270         
40271         this.setCurrency(currency);
40272     },
40273     
40274     setFromData : function(o)
40275     {
40276         var c = {};
40277         
40278         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40279         
40280         this.setFromCurrencyData(c);
40281         
40282         var value = '';
40283         
40284         if (this.name) {
40285             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40286         } else {
40287             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40288         }
40289         
40290         this.setValue(value);
40291         
40292     },
40293     
40294     setCurrency : function(v)
40295     {   
40296         this.currencyValue = v;
40297         
40298         if(this.rendered){
40299             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40300             this.validate();
40301         }
40302     },
40303     
40304     setValue : function(v)
40305     {
40306         v = this.fixPrecision(v);
40307         
40308         v = String(v).replace(".", this.decimalSeparator);
40309         
40310         this.value = v;
40311         
40312         if(this.rendered){
40313             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40314             this.validate();
40315         }
40316     },
40317     
40318     getRawValue : function()
40319     {
40320         var v = this.inputEl().getValue();
40321         
40322         return v;
40323     },
40324     
40325     getValue : function()
40326     {
40327         return this.fixPrecision(this.parseValue(this.getRawValue()));
40328     },
40329     
40330     parseValue : function(value)
40331     {
40332         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40333         return isNaN(value) ? '' : value;
40334     },
40335     
40336     fixPrecision : function(value)
40337     {
40338         var nan = isNaN(value);
40339         
40340         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40341             return nan ? '' : value;
40342         }
40343         
40344         return parseFloat(value).toFixed(this.decimalPrecision);
40345     },
40346     
40347     decimalPrecisionFcn : function(v)
40348     {
40349         return Math.floor(v);
40350     },
40351     
40352     validateValue : function(value)
40353     {
40354         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40355             return false;
40356         }
40357         
40358         var num = this.parseValue(value);
40359         
40360         if(isNaN(num)){
40361             this.markInvalid(String.format(this.nanText, value));
40362             return false;
40363         }
40364         
40365         if(num < this.minValue){
40366             this.markInvalid(String.format(this.minText, this.minValue));
40367             return false;
40368         }
40369         
40370         if(num > this.maxValue){
40371             this.markInvalid(String.format(this.maxText, this.maxValue));
40372             return false;
40373         }
40374         
40375         return true;
40376     },
40377     
40378     validate : function()
40379     {
40380         if(this.disabled){
40381             this.markValid();
40382             return true;
40383         }
40384         
40385         var currency = this.getCurrency();
40386         
40387         if(this.validateValue(this.getRawValue()) && currency.length){
40388             this.markValid();
40389             return true;
40390         }
40391         
40392         this.markInvalid();
40393         return false;
40394     },
40395     
40396     getName: function()
40397     {
40398         return this.name;
40399     },
40400     
40401     beforeBlur : function()
40402     {
40403         if(!this.castInt){
40404             return;
40405         }
40406         
40407         var v = this.parseValue(this.getRawValue());
40408         
40409         if(v){
40410             this.setValue(v);
40411         }
40412     },
40413     
40414     onBlur : function()
40415     {
40416         this.beforeBlur();
40417         
40418         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40419             //this.el.removeClass(this.focusClass);
40420         }
40421         
40422         this.hasFocus = false;
40423         
40424         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40425             this.validate();
40426         }
40427         
40428         var v = this.getValue();
40429         
40430         if(String(v) !== String(this.startValue)){
40431             this.fireEvent('change', this, v, this.startValue);
40432         }
40433         
40434         this.fireEvent("blur", this);
40435     },
40436     
40437     onCurrencyKeyUp : function(e)
40438     {
40439         Roo.log('on currency keyup');
40440         if(!e.isSpecialKey()){
40441             this.lastKey = e.getKey();
40442             this.dqTask.delay(this.queryDelay);
40443         }
40444     },
40445     
40446     inputEl : function()
40447     {
40448         return this.el.select('.roo-money-amount-input', true).first();
40449     },
40450     
40451     currencyEl : function()
40452     {
40453         return this.el.select('.roo-money-currency-input', true).first();
40454     },
40455     
40456     initQuery : function()
40457     {
40458         var v = this.getCurrency();
40459         Roo.log('initQuery???');
40460         this.doQuery(v);
40461     },
40462     
40463     onTypeAhead : function()
40464     {
40465         if(this.store.getCount() > 0){
40466             var r = this.store.getAt(0);
40467             var newValue = r.data[this.currencyField];
40468             var len = newValue.length;
40469             var selStart = this.getCurrency().length;
40470             
40471             if(selStart != len){
40472                 this.setCurrency(newValue);
40473                 this.selectText(selStart, newValue.length);
40474             }
40475         }
40476     },
40477     
40478     selectText : function(start, end)
40479     {
40480         var v = this.getCurrency();
40481         
40482         if(v.length > 0){
40483             start = start === undefined ? 0 : start;
40484             end = end === undefined ? v.length : end;
40485             var d = this.el.dom;
40486             if(d.setSelectionRange){
40487                 d.setSelectionRange(start, end);
40488             }else if(d.createTextRange){
40489                 var range = d.createTextRange();
40490                 range.moveStart("character", start);
40491                 range.moveEnd("character", v.length-end);
40492                 range.select();
40493             }
40494         }
40495     }
40496     
40497 });