417e7c12991e1eee2c439133417b7a21ee35407c
[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             
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                 this.selectedIndex = -1;
14111                 this.onLoad();   
14112             }
14113         }
14114         
14115         this.loadNext = false;
14116     },
14117     
14118     // private
14119     getParams : function(q){
14120         var p = {};
14121         //p[this.queryParam] = q;
14122         
14123         if(this.pageSize){
14124             p.start = 0;
14125             p.limit = this.pageSize;
14126         }
14127         return p;
14128     },
14129
14130     /**
14131      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14132      */
14133     collapse : function(){
14134         if(!this.isExpanded()){
14135             return;
14136         }
14137         
14138         this.list.hide();
14139         
14140         this.hasFocus = false;
14141         
14142         if(this.tickable){
14143             this.okBtn.hide();
14144             this.cancelBtn.hide();
14145             this.trigger.show();
14146             
14147             if(this.editable){
14148                 this.tickableInputEl().dom.value = '';
14149                 this.tickableInputEl().blur();
14150             }
14151             
14152         }
14153         
14154         Roo.get(document).un('mousedown', this.collapseIf, this);
14155         Roo.get(document).un('mousewheel', this.collapseIf, this);
14156         if (!this.editable) {
14157             Roo.get(document).un('keydown', this.listKeyPress, this);
14158         }
14159         this.fireEvent('collapse', this);
14160         
14161         this.validate();
14162     },
14163
14164     // private
14165     collapseIf : function(e){
14166         var in_combo  = e.within(this.el);
14167         var in_list =  e.within(this.list);
14168         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14169         
14170         if (in_combo || in_list || is_list) {
14171             //e.stopPropagation();
14172             return;
14173         }
14174         
14175         if(this.tickable){
14176             this.onTickableFooterButtonClick(e, false, false);
14177         }
14178
14179         this.collapse();
14180         
14181     },
14182
14183     /**
14184      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14185      */
14186     expand : function(){
14187        
14188         if(this.isExpanded() || !this.hasFocus){
14189             return;
14190         }
14191         
14192         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14193         this.list.setWidth(lw);
14194         
14195         Roo.log('expand');
14196         
14197         this.list.show();
14198         
14199         this.restrictHeight();
14200         
14201         if(this.tickable){
14202             
14203             this.tickItems = Roo.apply([], this.item);
14204             
14205             this.okBtn.show();
14206             this.cancelBtn.show();
14207             this.trigger.hide();
14208             
14209             if(this.editable){
14210                 this.tickableInputEl().focus();
14211             }
14212             
14213         }
14214         
14215         Roo.get(document).on('mousedown', this.collapseIf, this);
14216         Roo.get(document).on('mousewheel', this.collapseIf, this);
14217         if (!this.editable) {
14218             Roo.get(document).on('keydown', this.listKeyPress, this);
14219         }
14220         
14221         this.fireEvent('expand', this);
14222     },
14223
14224     // private
14225     // Implements the default empty TriggerField.onTriggerClick function
14226     onTriggerClick : function(e)
14227     {
14228         Roo.log('trigger click');
14229         
14230         if(this.disabled || !this.triggerList){
14231             return;
14232         }
14233         
14234         this.page = 0;
14235         this.loadNext = false;
14236         
14237         if(this.isExpanded()){
14238             this.collapse();
14239             if (!this.blockFocus) {
14240                 this.inputEl().focus();
14241             }
14242             
14243         }else {
14244             this.hasFocus = true;
14245             if(this.triggerAction == 'all') {
14246                 this.doQuery(this.allQuery, true);
14247             } else {
14248                 this.doQuery(this.getRawValue());
14249             }
14250             if (!this.blockFocus) {
14251                 this.inputEl().focus();
14252             }
14253         }
14254     },
14255     
14256     onTickableTriggerClick : function(e)
14257     {
14258         if(this.disabled){
14259             return;
14260         }
14261         
14262         this.page = 0;
14263         this.loadNext = false;
14264         this.hasFocus = true;
14265         
14266         if(this.triggerAction == 'all') {
14267             this.doQuery(this.allQuery, true);
14268         } else {
14269             this.doQuery(this.getRawValue());
14270         }
14271     },
14272     
14273     onSearchFieldClick : function(e)
14274     {
14275         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14276             this.onTickableFooterButtonClick(e, false, false);
14277             return;
14278         }
14279         
14280         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14281             return;
14282         }
14283         
14284         this.page = 0;
14285         this.loadNext = false;
14286         this.hasFocus = true;
14287         
14288         if(this.triggerAction == 'all') {
14289             this.doQuery(this.allQuery, true);
14290         } else {
14291             this.doQuery(this.getRawValue());
14292         }
14293     },
14294     
14295     listKeyPress : function(e)
14296     {
14297         //Roo.log('listkeypress');
14298         // scroll to first matching element based on key pres..
14299         if (e.isSpecialKey()) {
14300             return false;
14301         }
14302         var k = String.fromCharCode(e.getKey()).toUpperCase();
14303         //Roo.log(k);
14304         var match  = false;
14305         var csel = this.view.getSelectedNodes();
14306         var cselitem = false;
14307         if (csel.length) {
14308             var ix = this.view.indexOf(csel[0]);
14309             cselitem  = this.store.getAt(ix);
14310             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14311                 cselitem = false;
14312             }
14313             
14314         }
14315         
14316         this.store.each(function(v) { 
14317             if (cselitem) {
14318                 // start at existing selection.
14319                 if (cselitem.id == v.id) {
14320                     cselitem = false;
14321                 }
14322                 return true;
14323             }
14324                 
14325             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14326                 match = this.store.indexOf(v);
14327                 return false;
14328             }
14329             return true;
14330         }, this);
14331         
14332         if (match === false) {
14333             return true; // no more action?
14334         }
14335         // scroll to?
14336         this.view.select(match);
14337         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14338         sn.scrollIntoView(sn.dom.parentNode, false);
14339     },
14340     
14341     onViewScroll : function(e, t){
14342         
14343         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){
14344             return;
14345         }
14346         
14347         this.hasQuery = true;
14348         
14349         this.loading = this.list.select('.loading', true).first();
14350         
14351         if(this.loading === null){
14352             this.list.createChild({
14353                 tag: 'div',
14354                 cls: 'loading roo-select2-more-results roo-select2-active',
14355                 html: 'Loading more results...'
14356             });
14357             
14358             this.loading = this.list.select('.loading', true).first();
14359             
14360             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14361             
14362             this.loading.hide();
14363         }
14364         
14365         this.loading.show();
14366         
14367         var _combo = this;
14368         
14369         this.page++;
14370         this.loadNext = true;
14371         
14372         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14373         
14374         return;
14375     },
14376     
14377     addItem : function(o)
14378     {   
14379         var dv = ''; // display value
14380         
14381         if (this.displayField) {
14382             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14383         } else {
14384             // this is an error condition!!!
14385             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14386         }
14387         
14388         if(!dv.length){
14389             return;
14390         }
14391         
14392         var choice = this.choices.createChild({
14393             tag: 'li',
14394             cls: 'roo-select2-search-choice',
14395             cn: [
14396                 {
14397                     tag: 'div',
14398                     html: dv
14399                 },
14400                 {
14401                     tag: 'a',
14402                     href: '#',
14403                     cls: 'roo-select2-search-choice-close fa fa-times',
14404                     tabindex: '-1'
14405                 }
14406             ]
14407             
14408         }, this.searchField);
14409         
14410         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14411         
14412         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14413         
14414         this.item.push(o);
14415         
14416         this.lastData = o;
14417         
14418         this.syncValue();
14419         
14420         this.inputEl().dom.value = '';
14421         
14422         this.validate();
14423     },
14424     
14425     onRemoveItem : function(e, _self, o)
14426     {
14427         e.preventDefault();
14428         
14429         this.lastItem = Roo.apply([], this.item);
14430         
14431         var index = this.item.indexOf(o.data) * 1;
14432         
14433         if( index < 0){
14434             Roo.log('not this item?!');
14435             return;
14436         }
14437         
14438         this.item.splice(index, 1);
14439         o.item.remove();
14440         
14441         this.syncValue();
14442         
14443         this.fireEvent('remove', this, e);
14444         
14445         this.validate();
14446         
14447     },
14448     
14449     syncValue : function()
14450     {
14451         if(!this.item.length){
14452             this.clearValue();
14453             return;
14454         }
14455             
14456         var value = [];
14457         var _this = this;
14458         Roo.each(this.item, function(i){
14459             if(_this.valueField){
14460                 value.push(i[_this.valueField]);
14461                 return;
14462             }
14463
14464             value.push(i);
14465         });
14466
14467         this.value = value.join(',');
14468
14469         if(this.hiddenField){
14470             this.hiddenField.dom.value = this.value;
14471         }
14472         
14473         this.store.fireEvent("datachanged", this.store);
14474         
14475         this.validate();
14476     },
14477     
14478     clearItem : function()
14479     {
14480         if(!this.multiple){
14481             return;
14482         }
14483         
14484         this.item = [];
14485         
14486         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14487            c.remove();
14488         });
14489         
14490         this.syncValue();
14491         
14492         this.validate();
14493         
14494         if(this.tickable && !Roo.isTouch){
14495             this.view.refresh();
14496         }
14497     },
14498     
14499     inputEl: function ()
14500     {
14501         if(Roo.isIOS && this.useNativeIOS){
14502             return this.el.select('select.roo-ios-select', true).first();
14503         }
14504         
14505         if(Roo.isTouch && this.mobileTouchView){
14506             return this.el.select('input.form-control',true).first();
14507         }
14508         
14509         if(this.tickable){
14510             return this.searchField;
14511         }
14512         
14513         return this.el.select('input.form-control',true).first();
14514     },
14515     
14516     onTickableFooterButtonClick : function(e, btn, el)
14517     {
14518         e.preventDefault();
14519         
14520         this.lastItem = Roo.apply([], this.item);
14521         
14522         if(btn && btn.name == 'cancel'){
14523             this.tickItems = Roo.apply([], this.item);
14524             this.collapse();
14525             return;
14526         }
14527         
14528         this.clearItem();
14529         
14530         var _this = this;
14531         
14532         Roo.each(this.tickItems, function(o){
14533             _this.addItem(o);
14534         });
14535         
14536         this.collapse();
14537         
14538     },
14539     
14540     validate : function()
14541     {
14542         var v = this.getRawValue();
14543         
14544         if(this.multiple){
14545             v = this.getValue();
14546         }
14547         
14548         if(this.disabled || this.allowBlank || v.length){
14549             this.markValid();
14550             return true;
14551         }
14552         
14553         this.markInvalid();
14554         return false;
14555     },
14556     
14557     tickableInputEl : function()
14558     {
14559         if(!this.tickable || !this.editable){
14560             return this.inputEl();
14561         }
14562         
14563         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14564     },
14565     
14566     
14567     getAutoCreateTouchView : function()
14568     {
14569         var id = Roo.id();
14570         
14571         var cfg = {
14572             cls: 'form-group' //input-group
14573         };
14574         
14575         var input =  {
14576             tag: 'input',
14577             id : id,
14578             type : this.inputType,
14579             cls : 'form-control x-combo-noedit',
14580             autocomplete: 'new-password',
14581             placeholder : this.placeholder || '',
14582             readonly : true
14583         };
14584         
14585         if (this.name) {
14586             input.name = this.name;
14587         }
14588         
14589         if (this.size) {
14590             input.cls += ' input-' + this.size;
14591         }
14592         
14593         if (this.disabled) {
14594             input.disabled = true;
14595         }
14596         
14597         var inputblock = {
14598             cls : '',
14599             cn : [
14600                 input
14601             ]
14602         };
14603         
14604         if(this.before){
14605             inputblock.cls += ' input-group';
14606             
14607             inputblock.cn.unshift({
14608                 tag :'span',
14609                 cls : 'input-group-addon',
14610                 html : this.before
14611             });
14612         }
14613         
14614         if(this.removable && !this.multiple){
14615             inputblock.cls += ' roo-removable';
14616             
14617             inputblock.cn.push({
14618                 tag: 'button',
14619                 html : 'x',
14620                 cls : 'roo-combo-removable-btn close'
14621             });
14622         }
14623
14624         if(this.hasFeedback && !this.allowBlank){
14625             
14626             inputblock.cls += ' has-feedback';
14627             
14628             inputblock.cn.push({
14629                 tag: 'span',
14630                 cls: 'glyphicon form-control-feedback'
14631             });
14632             
14633         }
14634         
14635         if (this.after) {
14636             
14637             inputblock.cls += (this.before) ? '' : ' input-group';
14638             
14639             inputblock.cn.push({
14640                 tag :'span',
14641                 cls : 'input-group-addon',
14642                 html : this.after
14643             });
14644         }
14645
14646         var box = {
14647             tag: 'div',
14648             cn: [
14649                 {
14650                     tag: 'input',
14651                     type : 'hidden',
14652                     cls: 'form-hidden-field'
14653                 },
14654                 inputblock
14655             ]
14656             
14657         };
14658         
14659         if(this.multiple){
14660             box = {
14661                 tag: 'div',
14662                 cn: [
14663                     {
14664                         tag: 'input',
14665                         type : 'hidden',
14666                         cls: 'form-hidden-field'
14667                     },
14668                     {
14669                         tag: 'ul',
14670                         cls: 'roo-select2-choices',
14671                         cn:[
14672                             {
14673                                 tag: 'li',
14674                                 cls: 'roo-select2-search-field',
14675                                 cn: [
14676
14677                                     inputblock
14678                                 ]
14679                             }
14680                         ]
14681                     }
14682                 ]
14683             }
14684         };
14685         
14686         var combobox = {
14687             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14688             cn: [
14689                 box
14690             ]
14691         };
14692         
14693         if(!this.multiple && this.showToggleBtn){
14694             
14695             var caret = {
14696                         tag: 'span',
14697                         cls: 'caret'
14698             };
14699             
14700             if (this.caret != false) {
14701                 caret = {
14702                      tag: 'i',
14703                      cls: 'fa fa-' + this.caret
14704                 };
14705                 
14706             }
14707             
14708             combobox.cn.push({
14709                 tag :'span',
14710                 cls : 'input-group-addon btn dropdown-toggle',
14711                 cn : [
14712                     caret,
14713                     {
14714                         tag: 'span',
14715                         cls: 'combobox-clear',
14716                         cn  : [
14717                             {
14718                                 tag : 'i',
14719                                 cls: 'icon-remove'
14720                             }
14721                         ]
14722                     }
14723                 ]
14724
14725             })
14726         }
14727         
14728         if(this.multiple){
14729             combobox.cls += ' roo-select2-container-multi';
14730         }
14731         
14732         var align = this.labelAlign || this.parentLabelAlign();
14733         
14734         if (align ==='left' && this.fieldLabel.length) {
14735
14736             cfg.cn = [
14737                 {
14738                    tag : 'i',
14739                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14740                    tooltip : 'This field is required'
14741                 },
14742                 {
14743                     tag: 'label',
14744                     cls : 'control-label',
14745                     html : this.fieldLabel
14746
14747                 },
14748                 {
14749                     cls : '', 
14750                     cn: [
14751                         combobox
14752                     ]
14753                 }
14754             ];
14755             
14756             var labelCfg = cfg.cn[1];
14757             var contentCfg = cfg.cn[2];
14758             
14759
14760             if(this.indicatorpos == 'right'){
14761                 cfg.cn = [
14762                     {
14763                         tag: 'label',
14764                         'for' :  id,
14765                         cls : 'control-label',
14766                         cn : [
14767                             {
14768                                 tag : 'span',
14769                                 html : this.fieldLabel
14770                             },
14771                             {
14772                                 tag : 'i',
14773                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14774                                 tooltip : 'This field is required'
14775                             }
14776                         ]
14777                     },
14778                     {
14779                         cls : "",
14780                         cn: [
14781                             combobox
14782                         ]
14783                     }
14784
14785                 ];
14786                 
14787                 labelCfg = cfg.cn[0];
14788                 contentCfg = cfg.cn[1];
14789             }
14790             
14791            
14792             
14793             if(this.labelWidth > 12){
14794                 labelCfg.style = "width: " + this.labelWidth + 'px';
14795             }
14796             
14797             if(this.labelWidth < 13 && this.labelmd == 0){
14798                 this.labelmd = this.labelWidth;
14799             }
14800             
14801             if(this.labellg > 0){
14802                 labelCfg.cls += ' col-lg-' + this.labellg;
14803                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14804             }
14805             
14806             if(this.labelmd > 0){
14807                 labelCfg.cls += ' col-md-' + this.labelmd;
14808                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14809             }
14810             
14811             if(this.labelsm > 0){
14812                 labelCfg.cls += ' col-sm-' + this.labelsm;
14813                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14814             }
14815             
14816             if(this.labelxs > 0){
14817                 labelCfg.cls += ' col-xs-' + this.labelxs;
14818                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14819             }
14820                 
14821                 
14822         } else if ( this.fieldLabel.length) {
14823             cfg.cn = [
14824                 {
14825                    tag : 'i',
14826                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14827                    tooltip : 'This field is required'
14828                 },
14829                 {
14830                     tag: 'label',
14831                     cls : 'control-label',
14832                     html : this.fieldLabel
14833
14834                 },
14835                 {
14836                     cls : '', 
14837                     cn: [
14838                         combobox
14839                     ]
14840                 }
14841             ];
14842             
14843             if(this.indicatorpos == 'right'){
14844                 cfg.cn = [
14845                     {
14846                         tag: 'label',
14847                         cls : 'control-label',
14848                         html : this.fieldLabel,
14849                         cn : [
14850                             {
14851                                tag : 'i',
14852                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14853                                tooltip : 'This field is required'
14854                             }
14855                         ]
14856                     },
14857                     {
14858                         cls : '', 
14859                         cn: [
14860                             combobox
14861                         ]
14862                     }
14863                 ];
14864             }
14865         } else {
14866             cfg.cn = combobox;    
14867         }
14868         
14869         
14870         var settings = this;
14871         
14872         ['xs','sm','md','lg'].map(function(size){
14873             if (settings[size]) {
14874                 cfg.cls += ' col-' + size + '-' + settings[size];
14875             }
14876         });
14877         
14878         return cfg;
14879     },
14880     
14881     initTouchView : function()
14882     {
14883         this.renderTouchView();
14884         
14885         this.touchViewEl.on('scroll', function(){
14886             this.el.dom.scrollTop = 0;
14887         }, this);
14888         
14889         this.originalValue = this.getValue();
14890         
14891         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14892         
14893         this.inputEl().on("click", this.showTouchView, this);
14894         if (this.triggerEl) {
14895             this.triggerEl.on("click", this.showTouchView, this);
14896         }
14897         
14898         
14899         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14900         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14901         
14902         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14903         
14904         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14905         this.store.on('load', this.onTouchViewLoad, this);
14906         this.store.on('loadexception', this.onTouchViewLoadException, this);
14907         
14908         if(this.hiddenName){
14909             
14910             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14911             
14912             this.hiddenField.dom.value =
14913                 this.hiddenValue !== undefined ? this.hiddenValue :
14914                 this.value !== undefined ? this.value : '';
14915         
14916             this.el.dom.removeAttribute('name');
14917             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14918         }
14919         
14920         if(this.multiple){
14921             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14922             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14923         }
14924         
14925         if(this.removable && !this.multiple){
14926             var close = this.closeTriggerEl();
14927             if(close){
14928                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14929                 close.on('click', this.removeBtnClick, this, close);
14930             }
14931         }
14932         /*
14933          * fix the bug in Safari iOS8
14934          */
14935         this.inputEl().on("focus", function(e){
14936             document.activeElement.blur();
14937         }, this);
14938         
14939         return;
14940         
14941         
14942     },
14943     
14944     renderTouchView : function()
14945     {
14946         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14947         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14948         
14949         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14950         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14951         
14952         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14953         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14954         this.touchViewBodyEl.setStyle('overflow', 'auto');
14955         
14956         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14957         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14958         
14959         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14960         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14961         
14962     },
14963     
14964     showTouchView : function()
14965     {
14966         if(this.disabled){
14967             return;
14968         }
14969         
14970         this.touchViewHeaderEl.hide();
14971
14972         if(this.modalTitle.length){
14973             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14974             this.touchViewHeaderEl.show();
14975         }
14976
14977         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14978         this.touchViewEl.show();
14979
14980         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14981         
14982         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14983         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14984
14985         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14986
14987         if(this.modalTitle.length){
14988             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14989         }
14990         
14991         this.touchViewBodyEl.setHeight(bodyHeight);
14992
14993         if(this.animate){
14994             var _this = this;
14995             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14996         }else{
14997             this.touchViewEl.addClass('in');
14998         }
14999
15000         this.doTouchViewQuery();
15001         
15002     },
15003     
15004     hideTouchView : function()
15005     {
15006         this.touchViewEl.removeClass('in');
15007
15008         if(this.animate){
15009             var _this = this;
15010             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15011         }else{
15012             this.touchViewEl.setStyle('display', 'none');
15013         }
15014         
15015     },
15016     
15017     setTouchViewValue : function()
15018     {
15019         if(this.multiple){
15020             this.clearItem();
15021         
15022             var _this = this;
15023
15024             Roo.each(this.tickItems, function(o){
15025                 this.addItem(o);
15026             }, this);
15027         }
15028         
15029         this.hideTouchView();
15030     },
15031     
15032     doTouchViewQuery : function()
15033     {
15034         var qe = {
15035             query: '',
15036             forceAll: true,
15037             combo: this,
15038             cancel:false
15039         };
15040         
15041         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15042             return false;
15043         }
15044         
15045         if(!this.alwaysQuery || this.mode == 'local'){
15046             this.onTouchViewLoad();
15047             return;
15048         }
15049         
15050         this.store.load();
15051     },
15052     
15053     onTouchViewBeforeLoad : function(combo,opts)
15054     {
15055         return;
15056     },
15057
15058     // private
15059     onTouchViewLoad : function()
15060     {
15061         if(this.store.getCount() < 1){
15062             this.onTouchViewEmptyResults();
15063             return;
15064         }
15065         
15066         this.clearTouchView();
15067         
15068         var rawValue = this.getRawValue();
15069         
15070         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15071         
15072         this.tickItems = [];
15073         
15074         this.store.data.each(function(d, rowIndex){
15075             var row = this.touchViewListGroup.createChild(template);
15076             
15077             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15078                 row.addClass(d.data.cls);
15079             }
15080             
15081             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15082                 var cfg = {
15083                     data : d.data,
15084                     html : d.data[this.displayField]
15085                 };
15086                 
15087                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15088                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15089                 }
15090             }
15091             row.removeClass('selected');
15092             if(!this.multiple && this.valueField &&
15093                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15094             {
15095                 // radio buttons..
15096                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15097                 row.addClass('selected');
15098             }
15099             
15100             if(this.multiple && this.valueField &&
15101                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15102             {
15103                 
15104                 // checkboxes...
15105                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15106                 this.tickItems.push(d.data);
15107             }
15108             
15109             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15110             
15111         }, this);
15112         
15113         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15114         
15115         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15116
15117         if(this.modalTitle.length){
15118             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15119         }
15120
15121         var listHeight = this.touchViewListGroup.getHeight();
15122         
15123         var _this = this;
15124         
15125         if(firstChecked && listHeight > bodyHeight){
15126             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15127         }
15128         
15129     },
15130     
15131     onTouchViewLoadException : function()
15132     {
15133         this.hideTouchView();
15134     },
15135     
15136     onTouchViewEmptyResults : function()
15137     {
15138         this.clearTouchView();
15139         
15140         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15141         
15142         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15143         
15144     },
15145     
15146     clearTouchView : function()
15147     {
15148         this.touchViewListGroup.dom.innerHTML = '';
15149     },
15150     
15151     onTouchViewClick : function(e, el, o)
15152     {
15153         e.preventDefault();
15154         
15155         var row = o.row;
15156         var rowIndex = o.rowIndex;
15157         
15158         var r = this.store.getAt(rowIndex);
15159         
15160         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15161             
15162             if(!this.multiple){
15163                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15164                     c.dom.removeAttribute('checked');
15165                 }, this);
15166
15167                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15168
15169                 this.setFromData(r.data);
15170
15171                 var close = this.closeTriggerEl();
15172
15173                 if(close){
15174                     close.show();
15175                 }
15176
15177                 this.hideTouchView();
15178
15179                 this.fireEvent('select', this, r, rowIndex);
15180
15181                 return;
15182             }
15183
15184             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15185                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15186                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15187                 return;
15188             }
15189
15190             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15191             this.addItem(r.data);
15192             this.tickItems.push(r.data);
15193         }
15194     },
15195     
15196     getAutoCreateNativeIOS : function()
15197     {
15198         var cfg = {
15199             cls: 'form-group' //input-group,
15200         };
15201         
15202         var combobox =  {
15203             tag: 'select',
15204             cls : 'roo-ios-select'
15205         };
15206         
15207         if (this.name) {
15208             combobox.name = this.name;
15209         }
15210         
15211         if (this.disabled) {
15212             combobox.disabled = true;
15213         }
15214         
15215         var settings = this;
15216         
15217         ['xs','sm','md','lg'].map(function(size){
15218             if (settings[size]) {
15219                 cfg.cls += ' col-' + size + '-' + settings[size];
15220             }
15221         });
15222         
15223         cfg.cn = combobox;
15224         
15225         return cfg;
15226         
15227     },
15228     
15229     initIOSView : function()
15230     {
15231         this.store.on('load', this.onIOSViewLoad, this);
15232         
15233         return;
15234     },
15235     
15236     onIOSViewLoad : function()
15237     {
15238         if(this.store.getCount() < 1){
15239             return;
15240         }
15241         
15242         this.clearIOSView();
15243         
15244         if(this.allowBlank) {
15245             
15246             var default_text = '-- SELECT --';
15247             
15248             if(this.placeholder.length){
15249                 default_text = this.placeholder;
15250             }
15251             
15252             if(this.emptyTitle.length){
15253                 default_text += ' - ' + this.emptyTitle + ' -';
15254             }
15255             
15256             var opt = this.inputEl().createChild({
15257                 tag: 'option',
15258                 value : 0,
15259                 html : default_text
15260             });
15261             
15262             var o = {};
15263             o[this.valueField] = 0;
15264             o[this.displayField] = default_text;
15265             
15266             this.ios_options.push({
15267                 data : o,
15268                 el : opt
15269             });
15270             
15271         }
15272         
15273         this.store.data.each(function(d, rowIndex){
15274             
15275             var html = '';
15276             
15277             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15278                 html = d.data[this.displayField];
15279             }
15280             
15281             var value = '';
15282             
15283             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15284                 value = d.data[this.valueField];
15285             }
15286             
15287             var option = {
15288                 tag: 'option',
15289                 value : value,
15290                 html : html
15291             };
15292             
15293             if(this.value == d.data[this.valueField]){
15294                 option['selected'] = true;
15295             }
15296             
15297             var opt = this.inputEl().createChild(option);
15298             
15299             this.ios_options.push({
15300                 data : d.data,
15301                 el : opt
15302             });
15303             
15304         }, this);
15305         
15306         this.inputEl().on('change', function(){
15307            this.fireEvent('select', this);
15308         }, this);
15309         
15310     },
15311     
15312     clearIOSView: function()
15313     {
15314         this.inputEl().dom.innerHTML = '';
15315         
15316         this.ios_options = [];
15317     },
15318     
15319     setIOSValue: function(v)
15320     {
15321         this.value = v;
15322         
15323         if(!this.ios_options){
15324             return;
15325         }
15326         
15327         Roo.each(this.ios_options, function(opts){
15328            
15329            opts.el.dom.removeAttribute('selected');
15330            
15331            if(opts.data[this.valueField] != v){
15332                return;
15333            }
15334            
15335            opts.el.dom.setAttribute('selected', true);
15336            
15337         }, this);
15338     }
15339
15340     /** 
15341     * @cfg {Boolean} grow 
15342     * @hide 
15343     */
15344     /** 
15345     * @cfg {Number} growMin 
15346     * @hide 
15347     */
15348     /** 
15349     * @cfg {Number} growMax 
15350     * @hide 
15351     */
15352     /**
15353      * @hide
15354      * @method autoSize
15355      */
15356 });
15357
15358 Roo.apply(Roo.bootstrap.ComboBox,  {
15359     
15360     header : {
15361         tag: 'div',
15362         cls: 'modal-header',
15363         cn: [
15364             {
15365                 tag: 'h4',
15366                 cls: 'modal-title'
15367             }
15368         ]
15369     },
15370     
15371     body : {
15372         tag: 'div',
15373         cls: 'modal-body',
15374         cn: [
15375             {
15376                 tag: 'ul',
15377                 cls: 'list-group'
15378             }
15379         ]
15380     },
15381     
15382     listItemRadio : {
15383         tag: 'li',
15384         cls: 'list-group-item',
15385         cn: [
15386             {
15387                 tag: 'span',
15388                 cls: 'roo-combobox-list-group-item-value'
15389             },
15390             {
15391                 tag: 'div',
15392                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15393                 cn: [
15394                     {
15395                         tag: 'input',
15396                         type: 'radio'
15397                     },
15398                     {
15399                         tag: 'label'
15400                     }
15401                 ]
15402             }
15403         ]
15404     },
15405     
15406     listItemCheckbox : {
15407         tag: 'li',
15408         cls: 'list-group-item',
15409         cn: [
15410             {
15411                 tag: 'span',
15412                 cls: 'roo-combobox-list-group-item-value'
15413             },
15414             {
15415                 tag: 'div',
15416                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15417                 cn: [
15418                     {
15419                         tag: 'input',
15420                         type: 'checkbox'
15421                     },
15422                     {
15423                         tag: 'label'
15424                     }
15425                 ]
15426             }
15427         ]
15428     },
15429     
15430     emptyResult : {
15431         tag: 'div',
15432         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15433     },
15434     
15435     footer : {
15436         tag: 'div',
15437         cls: 'modal-footer',
15438         cn: [
15439             {
15440                 tag: 'div',
15441                 cls: 'row',
15442                 cn: [
15443                     {
15444                         tag: 'div',
15445                         cls: 'col-xs-6 text-left',
15446                         cn: {
15447                             tag: 'button',
15448                             cls: 'btn btn-danger roo-touch-view-cancel',
15449                             html: 'Cancel'
15450                         }
15451                     },
15452                     {
15453                         tag: 'div',
15454                         cls: 'col-xs-6 text-right',
15455                         cn: {
15456                             tag: 'button',
15457                             cls: 'btn btn-success roo-touch-view-ok',
15458                             html: 'OK'
15459                         }
15460                     }
15461                 ]
15462             }
15463         ]
15464         
15465     }
15466 });
15467
15468 Roo.apply(Roo.bootstrap.ComboBox,  {
15469     
15470     touchViewTemplate : {
15471         tag: 'div',
15472         cls: 'modal fade roo-combobox-touch-view',
15473         cn: [
15474             {
15475                 tag: 'div',
15476                 cls: 'modal-dialog',
15477                 style : 'position:fixed', // we have to fix position....
15478                 cn: [
15479                     {
15480                         tag: 'div',
15481                         cls: 'modal-content',
15482                         cn: [
15483                             Roo.bootstrap.ComboBox.header,
15484                             Roo.bootstrap.ComboBox.body,
15485                             Roo.bootstrap.ComboBox.footer
15486                         ]
15487                     }
15488                 ]
15489             }
15490         ]
15491     }
15492 });/*
15493  * Based on:
15494  * Ext JS Library 1.1.1
15495  * Copyright(c) 2006-2007, Ext JS, LLC.
15496  *
15497  * Originally Released Under LGPL - original licence link has changed is not relivant.
15498  *
15499  * Fork - LGPL
15500  * <script type="text/javascript">
15501  */
15502
15503 /**
15504  * @class Roo.View
15505  * @extends Roo.util.Observable
15506  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15507  * This class also supports single and multi selection modes. <br>
15508  * Create a data model bound view:
15509  <pre><code>
15510  var store = new Roo.data.Store(...);
15511
15512  var view = new Roo.View({
15513     el : "my-element",
15514     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15515  
15516     singleSelect: true,
15517     selectedClass: "ydataview-selected",
15518     store: store
15519  });
15520
15521  // listen for node click?
15522  view.on("click", function(vw, index, node, e){
15523  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15524  });
15525
15526  // load XML data
15527  dataModel.load("foobar.xml");
15528  </code></pre>
15529  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15530  * <br><br>
15531  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15532  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15533  * 
15534  * Note: old style constructor is still suported (container, template, config)
15535  * 
15536  * @constructor
15537  * Create a new View
15538  * @param {Object} config The config object
15539  * 
15540  */
15541 Roo.View = function(config, depreciated_tpl, depreciated_config){
15542     
15543     this.parent = false;
15544     
15545     if (typeof(depreciated_tpl) == 'undefined') {
15546         // new way.. - universal constructor.
15547         Roo.apply(this, config);
15548         this.el  = Roo.get(this.el);
15549     } else {
15550         // old format..
15551         this.el  = Roo.get(config);
15552         this.tpl = depreciated_tpl;
15553         Roo.apply(this, depreciated_config);
15554     }
15555     this.wrapEl  = this.el.wrap().wrap();
15556     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15557     
15558     
15559     if(typeof(this.tpl) == "string"){
15560         this.tpl = new Roo.Template(this.tpl);
15561     } else {
15562         // support xtype ctors..
15563         this.tpl = new Roo.factory(this.tpl, Roo);
15564     }
15565     
15566     
15567     this.tpl.compile();
15568     
15569     /** @private */
15570     this.addEvents({
15571         /**
15572          * @event beforeclick
15573          * Fires before a click is processed. Returns false to cancel the default action.
15574          * @param {Roo.View} this
15575          * @param {Number} index The index of the target node
15576          * @param {HTMLElement} node The target node
15577          * @param {Roo.EventObject} e The raw event object
15578          */
15579             "beforeclick" : true,
15580         /**
15581          * @event click
15582          * Fires when a template node is clicked.
15583          * @param {Roo.View} this
15584          * @param {Number} index The index of the target node
15585          * @param {HTMLElement} node The target node
15586          * @param {Roo.EventObject} e The raw event object
15587          */
15588             "click" : true,
15589         /**
15590          * @event dblclick
15591          * Fires when a template node is double clicked.
15592          * @param {Roo.View} this
15593          * @param {Number} index The index of the target node
15594          * @param {HTMLElement} node The target node
15595          * @param {Roo.EventObject} e The raw event object
15596          */
15597             "dblclick" : true,
15598         /**
15599          * @event contextmenu
15600          * Fires when a template node is right clicked.
15601          * @param {Roo.View} this
15602          * @param {Number} index The index of the target node
15603          * @param {HTMLElement} node The target node
15604          * @param {Roo.EventObject} e The raw event object
15605          */
15606             "contextmenu" : true,
15607         /**
15608          * @event selectionchange
15609          * Fires when the selected nodes change.
15610          * @param {Roo.View} this
15611          * @param {Array} selections Array of the selected nodes
15612          */
15613             "selectionchange" : true,
15614     
15615         /**
15616          * @event beforeselect
15617          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15618          * @param {Roo.View} this
15619          * @param {HTMLElement} node The node to be selected
15620          * @param {Array} selections Array of currently selected nodes
15621          */
15622             "beforeselect" : true,
15623         /**
15624          * @event preparedata
15625          * Fires on every row to render, to allow you to change the data.
15626          * @param {Roo.View} this
15627          * @param {Object} data to be rendered (change this)
15628          */
15629           "preparedata" : true
15630           
15631           
15632         });
15633
15634
15635
15636     this.el.on({
15637         "click": this.onClick,
15638         "dblclick": this.onDblClick,
15639         "contextmenu": this.onContextMenu,
15640         scope:this
15641     });
15642
15643     this.selections = [];
15644     this.nodes = [];
15645     this.cmp = new Roo.CompositeElementLite([]);
15646     if(this.store){
15647         this.store = Roo.factory(this.store, Roo.data);
15648         this.setStore(this.store, true);
15649     }
15650     
15651     if ( this.footer && this.footer.xtype) {
15652            
15653          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15654         
15655         this.footer.dataSource = this.store;
15656         this.footer.container = fctr;
15657         this.footer = Roo.factory(this.footer, Roo);
15658         fctr.insertFirst(this.el);
15659         
15660         // this is a bit insane - as the paging toolbar seems to detach the el..
15661 //        dom.parentNode.parentNode.parentNode
15662          // they get detached?
15663     }
15664     
15665     
15666     Roo.View.superclass.constructor.call(this);
15667     
15668     
15669 };
15670
15671 Roo.extend(Roo.View, Roo.util.Observable, {
15672     
15673      /**
15674      * @cfg {Roo.data.Store} store Data store to load data from.
15675      */
15676     store : false,
15677     
15678     /**
15679      * @cfg {String|Roo.Element} el The container element.
15680      */
15681     el : '',
15682     
15683     /**
15684      * @cfg {String|Roo.Template} tpl The template used by this View 
15685      */
15686     tpl : false,
15687     /**
15688      * @cfg {String} dataName the named area of the template to use as the data area
15689      *                          Works with domtemplates roo-name="name"
15690      */
15691     dataName: false,
15692     /**
15693      * @cfg {String} selectedClass The css class to add to selected nodes
15694      */
15695     selectedClass : "x-view-selected",
15696      /**
15697      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15698      */
15699     emptyText : "",
15700     
15701     /**
15702      * @cfg {String} text to display on mask (default Loading)
15703      */
15704     mask : false,
15705     /**
15706      * @cfg {Boolean} multiSelect Allow multiple selection
15707      */
15708     multiSelect : false,
15709     /**
15710      * @cfg {Boolean} singleSelect Allow single selection
15711      */
15712     singleSelect:  false,
15713     
15714     /**
15715      * @cfg {Boolean} toggleSelect - selecting 
15716      */
15717     toggleSelect : false,
15718     
15719     /**
15720      * @cfg {Boolean} tickable - selecting 
15721      */
15722     tickable : false,
15723     
15724     /**
15725      * Returns the element this view is bound to.
15726      * @return {Roo.Element}
15727      */
15728     getEl : function(){
15729         return this.wrapEl;
15730     },
15731     
15732     
15733
15734     /**
15735      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15736      */
15737     refresh : function(){
15738         //Roo.log('refresh');
15739         var t = this.tpl;
15740         
15741         // if we are using something like 'domtemplate', then
15742         // the what gets used is:
15743         // t.applySubtemplate(NAME, data, wrapping data..)
15744         // the outer template then get' applied with
15745         //     the store 'extra data'
15746         // and the body get's added to the
15747         //      roo-name="data" node?
15748         //      <span class='roo-tpl-{name}'></span> ?????
15749         
15750         
15751         
15752         this.clearSelections();
15753         this.el.update("");
15754         var html = [];
15755         var records = this.store.getRange();
15756         if(records.length < 1) {
15757             
15758             // is this valid??  = should it render a template??
15759             
15760             this.el.update(this.emptyText);
15761             return;
15762         }
15763         var el = this.el;
15764         if (this.dataName) {
15765             this.el.update(t.apply(this.store.meta)); //????
15766             el = this.el.child('.roo-tpl-' + this.dataName);
15767         }
15768         
15769         for(var i = 0, len = records.length; i < len; i++){
15770             var data = this.prepareData(records[i].data, i, records[i]);
15771             this.fireEvent("preparedata", this, data, i, records[i]);
15772             
15773             var d = Roo.apply({}, data);
15774             
15775             if(this.tickable){
15776                 Roo.apply(d, {'roo-id' : Roo.id()});
15777                 
15778                 var _this = this;
15779             
15780                 Roo.each(this.parent.item, function(item){
15781                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15782                         return;
15783                     }
15784                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15785                 });
15786             }
15787             
15788             html[html.length] = Roo.util.Format.trim(
15789                 this.dataName ?
15790                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15791                     t.apply(d)
15792             );
15793         }
15794         
15795         
15796         
15797         el.update(html.join(""));
15798         this.nodes = el.dom.childNodes;
15799         this.updateIndexes(0);
15800     },
15801     
15802
15803     /**
15804      * Function to override to reformat the data that is sent to
15805      * the template for each node.
15806      * DEPRICATED - use the preparedata event handler.
15807      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15808      * a JSON object for an UpdateManager bound view).
15809      */
15810     prepareData : function(data, index, record)
15811     {
15812         this.fireEvent("preparedata", this, data, index, record);
15813         return data;
15814     },
15815
15816     onUpdate : function(ds, record){
15817         // Roo.log('on update');   
15818         this.clearSelections();
15819         var index = this.store.indexOf(record);
15820         var n = this.nodes[index];
15821         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15822         n.parentNode.removeChild(n);
15823         this.updateIndexes(index, index);
15824     },
15825
15826     
15827     
15828 // --------- FIXME     
15829     onAdd : function(ds, records, index)
15830     {
15831         //Roo.log(['on Add', ds, records, index] );        
15832         this.clearSelections();
15833         if(this.nodes.length == 0){
15834             this.refresh();
15835             return;
15836         }
15837         var n = this.nodes[index];
15838         for(var i = 0, len = records.length; i < len; i++){
15839             var d = this.prepareData(records[i].data, i, records[i]);
15840             if(n){
15841                 this.tpl.insertBefore(n, d);
15842             }else{
15843                 
15844                 this.tpl.append(this.el, d);
15845             }
15846         }
15847         this.updateIndexes(index);
15848     },
15849
15850     onRemove : function(ds, record, index){
15851        // Roo.log('onRemove');
15852         this.clearSelections();
15853         var el = this.dataName  ?
15854             this.el.child('.roo-tpl-' + this.dataName) :
15855             this.el; 
15856         
15857         el.dom.removeChild(this.nodes[index]);
15858         this.updateIndexes(index);
15859     },
15860
15861     /**
15862      * Refresh an individual node.
15863      * @param {Number} index
15864      */
15865     refreshNode : function(index){
15866         this.onUpdate(this.store, this.store.getAt(index));
15867     },
15868
15869     updateIndexes : function(startIndex, endIndex){
15870         var ns = this.nodes;
15871         startIndex = startIndex || 0;
15872         endIndex = endIndex || ns.length - 1;
15873         for(var i = startIndex; i <= endIndex; i++){
15874             ns[i].nodeIndex = i;
15875         }
15876     },
15877
15878     /**
15879      * Changes the data store this view uses and refresh the view.
15880      * @param {Store} store
15881      */
15882     setStore : function(store, initial){
15883         if(!initial && this.store){
15884             this.store.un("datachanged", this.refresh);
15885             this.store.un("add", this.onAdd);
15886             this.store.un("remove", this.onRemove);
15887             this.store.un("update", this.onUpdate);
15888             this.store.un("clear", this.refresh);
15889             this.store.un("beforeload", this.onBeforeLoad);
15890             this.store.un("load", this.onLoad);
15891             this.store.un("loadexception", this.onLoad);
15892         }
15893         if(store){
15894           
15895             store.on("datachanged", this.refresh, this);
15896             store.on("add", this.onAdd, this);
15897             store.on("remove", this.onRemove, this);
15898             store.on("update", this.onUpdate, this);
15899             store.on("clear", this.refresh, this);
15900             store.on("beforeload", this.onBeforeLoad, this);
15901             store.on("load", this.onLoad, this);
15902             store.on("loadexception", this.onLoad, this);
15903         }
15904         
15905         if(store){
15906             this.refresh();
15907         }
15908     },
15909     /**
15910      * onbeforeLoad - masks the loading area.
15911      *
15912      */
15913     onBeforeLoad : function(store,opts)
15914     {
15915          //Roo.log('onBeforeLoad');   
15916         if (!opts.add) {
15917             this.el.update("");
15918         }
15919         this.el.mask(this.mask ? this.mask : "Loading" ); 
15920     },
15921     onLoad : function ()
15922     {
15923         this.el.unmask();
15924     },
15925     
15926
15927     /**
15928      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15929      * @param {HTMLElement} node
15930      * @return {HTMLElement} The template node
15931      */
15932     findItemFromChild : function(node){
15933         var el = this.dataName  ?
15934             this.el.child('.roo-tpl-' + this.dataName,true) :
15935             this.el.dom; 
15936         
15937         if(!node || node.parentNode == el){
15938                     return node;
15939             }
15940             var p = node.parentNode;
15941             while(p && p != el){
15942             if(p.parentNode == el){
15943                 return p;
15944             }
15945             p = p.parentNode;
15946         }
15947             return null;
15948     },
15949
15950     /** @ignore */
15951     onClick : function(e){
15952         var item = this.findItemFromChild(e.getTarget());
15953         if(item){
15954             var index = this.indexOf(item);
15955             if(this.onItemClick(item, index, e) !== false){
15956                 this.fireEvent("click", this, index, item, e);
15957             }
15958         }else{
15959             this.clearSelections();
15960         }
15961     },
15962
15963     /** @ignore */
15964     onContextMenu : function(e){
15965         var item = this.findItemFromChild(e.getTarget());
15966         if(item){
15967             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15968         }
15969     },
15970
15971     /** @ignore */
15972     onDblClick : function(e){
15973         var item = this.findItemFromChild(e.getTarget());
15974         if(item){
15975             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15976         }
15977     },
15978
15979     onItemClick : function(item, index, e)
15980     {
15981         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15982             return false;
15983         }
15984         if (this.toggleSelect) {
15985             var m = this.isSelected(item) ? 'unselect' : 'select';
15986             //Roo.log(m);
15987             var _t = this;
15988             _t[m](item, true, false);
15989             return true;
15990         }
15991         if(this.multiSelect || this.singleSelect){
15992             if(this.multiSelect && e.shiftKey && this.lastSelection){
15993                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15994             }else{
15995                 this.select(item, this.multiSelect && e.ctrlKey);
15996                 this.lastSelection = item;
15997             }
15998             
15999             if(!this.tickable){
16000                 e.preventDefault();
16001             }
16002             
16003         }
16004         return true;
16005     },
16006
16007     /**
16008      * Get the number of selected nodes.
16009      * @return {Number}
16010      */
16011     getSelectionCount : function(){
16012         return this.selections.length;
16013     },
16014
16015     /**
16016      * Get the currently selected nodes.
16017      * @return {Array} An array of HTMLElements
16018      */
16019     getSelectedNodes : function(){
16020         return this.selections;
16021     },
16022
16023     /**
16024      * Get the indexes of the selected nodes.
16025      * @return {Array}
16026      */
16027     getSelectedIndexes : function(){
16028         var indexes = [], s = this.selections;
16029         for(var i = 0, len = s.length; i < len; i++){
16030             indexes.push(s[i].nodeIndex);
16031         }
16032         return indexes;
16033     },
16034
16035     /**
16036      * Clear all selections
16037      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16038      */
16039     clearSelections : function(suppressEvent){
16040         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16041             this.cmp.elements = this.selections;
16042             this.cmp.removeClass(this.selectedClass);
16043             this.selections = [];
16044             if(!suppressEvent){
16045                 this.fireEvent("selectionchange", this, this.selections);
16046             }
16047         }
16048     },
16049
16050     /**
16051      * Returns true if the passed node is selected
16052      * @param {HTMLElement/Number} node The node or node index
16053      * @return {Boolean}
16054      */
16055     isSelected : function(node){
16056         var s = this.selections;
16057         if(s.length < 1){
16058             return false;
16059         }
16060         node = this.getNode(node);
16061         return s.indexOf(node) !== -1;
16062     },
16063
16064     /**
16065      * Selects nodes.
16066      * @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
16067      * @param {Boolean} keepExisting (optional) true to keep existing selections
16068      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16069      */
16070     select : function(nodeInfo, keepExisting, suppressEvent){
16071         if(nodeInfo instanceof Array){
16072             if(!keepExisting){
16073                 this.clearSelections(true);
16074             }
16075             for(var i = 0, len = nodeInfo.length; i < len; i++){
16076                 this.select(nodeInfo[i], true, true);
16077             }
16078             return;
16079         } 
16080         var node = this.getNode(nodeInfo);
16081         if(!node || this.isSelected(node)){
16082             return; // already selected.
16083         }
16084         if(!keepExisting){
16085             this.clearSelections(true);
16086         }
16087         
16088         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16089             Roo.fly(node).addClass(this.selectedClass);
16090             this.selections.push(node);
16091             if(!suppressEvent){
16092                 this.fireEvent("selectionchange", this, this.selections);
16093             }
16094         }
16095         
16096         
16097     },
16098       /**
16099      * Unselects nodes.
16100      * @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
16101      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16102      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16103      */
16104     unselect : function(nodeInfo, keepExisting, suppressEvent)
16105     {
16106         if(nodeInfo instanceof Array){
16107             Roo.each(this.selections, function(s) {
16108                 this.unselect(s, nodeInfo);
16109             }, this);
16110             return;
16111         }
16112         var node = this.getNode(nodeInfo);
16113         if(!node || !this.isSelected(node)){
16114             //Roo.log("not selected");
16115             return; // not selected.
16116         }
16117         // fireevent???
16118         var ns = [];
16119         Roo.each(this.selections, function(s) {
16120             if (s == node ) {
16121                 Roo.fly(node).removeClass(this.selectedClass);
16122
16123                 return;
16124             }
16125             ns.push(s);
16126         },this);
16127         
16128         this.selections= ns;
16129         this.fireEvent("selectionchange", this, this.selections);
16130     },
16131
16132     /**
16133      * Gets a template node.
16134      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16135      * @return {HTMLElement} The node or null if it wasn't found
16136      */
16137     getNode : function(nodeInfo){
16138         if(typeof nodeInfo == "string"){
16139             return document.getElementById(nodeInfo);
16140         }else if(typeof nodeInfo == "number"){
16141             return this.nodes[nodeInfo];
16142         }
16143         return nodeInfo;
16144     },
16145
16146     /**
16147      * Gets a range template nodes.
16148      * @param {Number} startIndex
16149      * @param {Number} endIndex
16150      * @return {Array} An array of nodes
16151      */
16152     getNodes : function(start, end){
16153         var ns = this.nodes;
16154         start = start || 0;
16155         end = typeof end == "undefined" ? ns.length - 1 : end;
16156         var nodes = [];
16157         if(start <= end){
16158             for(var i = start; i <= end; i++){
16159                 nodes.push(ns[i]);
16160             }
16161         } else{
16162             for(var i = start; i >= end; i--){
16163                 nodes.push(ns[i]);
16164             }
16165         }
16166         return nodes;
16167     },
16168
16169     /**
16170      * Finds the index of the passed node
16171      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16172      * @return {Number} The index of the node or -1
16173      */
16174     indexOf : function(node){
16175         node = this.getNode(node);
16176         if(typeof node.nodeIndex == "number"){
16177             return node.nodeIndex;
16178         }
16179         var ns = this.nodes;
16180         for(var i = 0, len = ns.length; i < len; i++){
16181             if(ns[i] == node){
16182                 return i;
16183             }
16184         }
16185         return -1;
16186     }
16187 });
16188 /*
16189  * - LGPL
16190  *
16191  * based on jquery fullcalendar
16192  * 
16193  */
16194
16195 Roo.bootstrap = Roo.bootstrap || {};
16196 /**
16197  * @class Roo.bootstrap.Calendar
16198  * @extends Roo.bootstrap.Component
16199  * Bootstrap Calendar class
16200  * @cfg {Boolean} loadMask (true|false) default false
16201  * @cfg {Object} header generate the user specific header of the calendar, default false
16202
16203  * @constructor
16204  * Create a new Container
16205  * @param {Object} config The config object
16206  */
16207
16208
16209
16210 Roo.bootstrap.Calendar = function(config){
16211     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16212      this.addEvents({
16213         /**
16214              * @event select
16215              * Fires when a date is selected
16216              * @param {DatePicker} this
16217              * @param {Date} date The selected date
16218              */
16219         'select': true,
16220         /**
16221              * @event monthchange
16222              * Fires when the displayed month changes 
16223              * @param {DatePicker} this
16224              * @param {Date} date The selected month
16225              */
16226         'monthchange': true,
16227         /**
16228              * @event evententer
16229              * Fires when mouse over an event
16230              * @param {Calendar} this
16231              * @param {event} Event
16232              */
16233         'evententer': true,
16234         /**
16235              * @event eventleave
16236              * Fires when the mouse leaves an
16237              * @param {Calendar} this
16238              * @param {event}
16239              */
16240         'eventleave': true,
16241         /**
16242              * @event eventclick
16243              * Fires when the mouse click an
16244              * @param {Calendar} this
16245              * @param {event}
16246              */
16247         'eventclick': true
16248         
16249     });
16250
16251 };
16252
16253 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16254     
16255      /**
16256      * @cfg {Number} startDay
16257      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16258      */
16259     startDay : 0,
16260     
16261     loadMask : false,
16262     
16263     header : false,
16264       
16265     getAutoCreate : function(){
16266         
16267         
16268         var fc_button = function(name, corner, style, content ) {
16269             return Roo.apply({},{
16270                 tag : 'span',
16271                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16272                          (corner.length ?
16273                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16274                             ''
16275                         ),
16276                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16277                 unselectable: 'on'
16278             });
16279         };
16280         
16281         var header = {};
16282         
16283         if(!this.header){
16284             header = {
16285                 tag : 'table',
16286                 cls : 'fc-header',
16287                 style : 'width:100%',
16288                 cn : [
16289                     {
16290                         tag: 'tr',
16291                         cn : [
16292                             {
16293                                 tag : 'td',
16294                                 cls : 'fc-header-left',
16295                                 cn : [
16296                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16297                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16298                                     { tag: 'span', cls: 'fc-header-space' },
16299                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16300
16301
16302                                 ]
16303                             },
16304
16305                             {
16306                                 tag : 'td',
16307                                 cls : 'fc-header-center',
16308                                 cn : [
16309                                     {
16310                                         tag: 'span',
16311                                         cls: 'fc-header-title',
16312                                         cn : {
16313                                             tag: 'H2',
16314                                             html : 'month / year'
16315                                         }
16316                                     }
16317
16318                                 ]
16319                             },
16320                             {
16321                                 tag : 'td',
16322                                 cls : 'fc-header-right',
16323                                 cn : [
16324                               /*      fc_button('month', 'left', '', 'month' ),
16325                                     fc_button('week', '', '', 'week' ),
16326                                     fc_button('day', 'right', '', 'day' )
16327                                 */    
16328
16329                                 ]
16330                             }
16331
16332                         ]
16333                     }
16334                 ]
16335             };
16336         }
16337         
16338         header = this.header;
16339         
16340        
16341         var cal_heads = function() {
16342             var ret = [];
16343             // fixme - handle this.
16344             
16345             for (var i =0; i < Date.dayNames.length; i++) {
16346                 var d = Date.dayNames[i];
16347                 ret.push({
16348                     tag: 'th',
16349                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16350                     html : d.substring(0,3)
16351                 });
16352                 
16353             }
16354             ret[0].cls += ' fc-first';
16355             ret[6].cls += ' fc-last';
16356             return ret;
16357         };
16358         var cal_cell = function(n) {
16359             return  {
16360                 tag: 'td',
16361                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16362                 cn : [
16363                     {
16364                         cn : [
16365                             {
16366                                 cls: 'fc-day-number',
16367                                 html: 'D'
16368                             },
16369                             {
16370                                 cls: 'fc-day-content',
16371                              
16372                                 cn : [
16373                                      {
16374                                         style: 'position: relative;' // height: 17px;
16375                                     }
16376                                 ]
16377                             }
16378                             
16379                             
16380                         ]
16381                     }
16382                 ]
16383                 
16384             }
16385         };
16386         var cal_rows = function() {
16387             
16388             var ret = [];
16389             for (var r = 0; r < 6; r++) {
16390                 var row= {
16391                     tag : 'tr',
16392                     cls : 'fc-week',
16393                     cn : []
16394                 };
16395                 
16396                 for (var i =0; i < Date.dayNames.length; i++) {
16397                     var d = Date.dayNames[i];
16398                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16399
16400                 }
16401                 row.cn[0].cls+=' fc-first';
16402                 row.cn[0].cn[0].style = 'min-height:90px';
16403                 row.cn[6].cls+=' fc-last';
16404                 ret.push(row);
16405                 
16406             }
16407             ret[0].cls += ' fc-first';
16408             ret[4].cls += ' fc-prev-last';
16409             ret[5].cls += ' fc-last';
16410             return ret;
16411             
16412         };
16413         
16414         var cal_table = {
16415             tag: 'table',
16416             cls: 'fc-border-separate',
16417             style : 'width:100%',
16418             cellspacing  : 0,
16419             cn : [
16420                 { 
16421                     tag: 'thead',
16422                     cn : [
16423                         { 
16424                             tag: 'tr',
16425                             cls : 'fc-first fc-last',
16426                             cn : cal_heads()
16427                         }
16428                     ]
16429                 },
16430                 { 
16431                     tag: 'tbody',
16432                     cn : cal_rows()
16433                 }
16434                   
16435             ]
16436         };
16437          
16438          var cfg = {
16439             cls : 'fc fc-ltr',
16440             cn : [
16441                 header,
16442                 {
16443                     cls : 'fc-content',
16444                     style : "position: relative;",
16445                     cn : [
16446                         {
16447                             cls : 'fc-view fc-view-month fc-grid',
16448                             style : 'position: relative',
16449                             unselectable : 'on',
16450                             cn : [
16451                                 {
16452                                     cls : 'fc-event-container',
16453                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16454                                 },
16455                                 cal_table
16456                             ]
16457                         }
16458                     ]
16459     
16460                 }
16461            ] 
16462             
16463         };
16464         
16465          
16466         
16467         return cfg;
16468     },
16469     
16470     
16471     initEvents : function()
16472     {
16473         if(!this.store){
16474             throw "can not find store for calendar";
16475         }
16476         
16477         var mark = {
16478             tag: "div",
16479             cls:"x-dlg-mask",
16480             style: "text-align:center",
16481             cn: [
16482                 {
16483                     tag: "div",
16484                     style: "background-color:white;width:50%;margin:250 auto",
16485                     cn: [
16486                         {
16487                             tag: "img",
16488                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16489                         },
16490                         {
16491                             tag: "span",
16492                             html: "Loading"
16493                         }
16494                         
16495                     ]
16496                 }
16497             ]
16498         };
16499         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16500         
16501         var size = this.el.select('.fc-content', true).first().getSize();
16502         this.maskEl.setSize(size.width, size.height);
16503         this.maskEl.enableDisplayMode("block");
16504         if(!this.loadMask){
16505             this.maskEl.hide();
16506         }
16507         
16508         this.store = Roo.factory(this.store, Roo.data);
16509         this.store.on('load', this.onLoad, this);
16510         this.store.on('beforeload', this.onBeforeLoad, this);
16511         
16512         this.resize();
16513         
16514         this.cells = this.el.select('.fc-day',true);
16515         //Roo.log(this.cells);
16516         this.textNodes = this.el.query('.fc-day-number');
16517         this.cells.addClassOnOver('fc-state-hover');
16518         
16519         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16520         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16521         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16522         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16523         
16524         this.on('monthchange', this.onMonthChange, this);
16525         
16526         this.update(new Date().clearTime());
16527     },
16528     
16529     resize : function() {
16530         var sz  = this.el.getSize();
16531         
16532         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16533         this.el.select('.fc-day-content div',true).setHeight(34);
16534     },
16535     
16536     
16537     // private
16538     showPrevMonth : function(e){
16539         this.update(this.activeDate.add("mo", -1));
16540     },
16541     showToday : function(e){
16542         this.update(new Date().clearTime());
16543     },
16544     // private
16545     showNextMonth : function(e){
16546         this.update(this.activeDate.add("mo", 1));
16547     },
16548
16549     // private
16550     showPrevYear : function(){
16551         this.update(this.activeDate.add("y", -1));
16552     },
16553
16554     // private
16555     showNextYear : function(){
16556         this.update(this.activeDate.add("y", 1));
16557     },
16558
16559     
16560    // private
16561     update : function(date)
16562     {
16563         var vd = this.activeDate;
16564         this.activeDate = date;
16565 //        if(vd && this.el){
16566 //            var t = date.getTime();
16567 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16568 //                Roo.log('using add remove');
16569 //                
16570 //                this.fireEvent('monthchange', this, date);
16571 //                
16572 //                this.cells.removeClass("fc-state-highlight");
16573 //                this.cells.each(function(c){
16574 //                   if(c.dateValue == t){
16575 //                       c.addClass("fc-state-highlight");
16576 //                       setTimeout(function(){
16577 //                            try{c.dom.firstChild.focus();}catch(e){}
16578 //                       }, 50);
16579 //                       return false;
16580 //                   }
16581 //                   return true;
16582 //                });
16583 //                return;
16584 //            }
16585 //        }
16586         
16587         var days = date.getDaysInMonth();
16588         
16589         var firstOfMonth = date.getFirstDateOfMonth();
16590         var startingPos = firstOfMonth.getDay()-this.startDay;
16591         
16592         if(startingPos < this.startDay){
16593             startingPos += 7;
16594         }
16595         
16596         var pm = date.add(Date.MONTH, -1);
16597         var prevStart = pm.getDaysInMonth()-startingPos;
16598 //        
16599         this.cells = this.el.select('.fc-day',true);
16600         this.textNodes = this.el.query('.fc-day-number');
16601         this.cells.addClassOnOver('fc-state-hover');
16602         
16603         var cells = this.cells.elements;
16604         var textEls = this.textNodes;
16605         
16606         Roo.each(cells, function(cell){
16607             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16608         });
16609         
16610         days += startingPos;
16611
16612         // convert everything to numbers so it's fast
16613         var day = 86400000;
16614         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16615         //Roo.log(d);
16616         //Roo.log(pm);
16617         //Roo.log(prevStart);
16618         
16619         var today = new Date().clearTime().getTime();
16620         var sel = date.clearTime().getTime();
16621         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16622         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16623         var ddMatch = this.disabledDatesRE;
16624         var ddText = this.disabledDatesText;
16625         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16626         var ddaysText = this.disabledDaysText;
16627         var format = this.format;
16628         
16629         var setCellClass = function(cal, cell){
16630             cell.row = 0;
16631             cell.events = [];
16632             cell.more = [];
16633             //Roo.log('set Cell Class');
16634             cell.title = "";
16635             var t = d.getTime();
16636             
16637             //Roo.log(d);
16638             
16639             cell.dateValue = t;
16640             if(t == today){
16641                 cell.className += " fc-today";
16642                 cell.className += " fc-state-highlight";
16643                 cell.title = cal.todayText;
16644             }
16645             if(t == sel){
16646                 // disable highlight in other month..
16647                 //cell.className += " fc-state-highlight";
16648                 
16649             }
16650             // disabling
16651             if(t < min) {
16652                 cell.className = " fc-state-disabled";
16653                 cell.title = cal.minText;
16654                 return;
16655             }
16656             if(t > max) {
16657                 cell.className = " fc-state-disabled";
16658                 cell.title = cal.maxText;
16659                 return;
16660             }
16661             if(ddays){
16662                 if(ddays.indexOf(d.getDay()) != -1){
16663                     cell.title = ddaysText;
16664                     cell.className = " fc-state-disabled";
16665                 }
16666             }
16667             if(ddMatch && format){
16668                 var fvalue = d.dateFormat(format);
16669                 if(ddMatch.test(fvalue)){
16670                     cell.title = ddText.replace("%0", fvalue);
16671                     cell.className = " fc-state-disabled";
16672                 }
16673             }
16674             
16675             if (!cell.initialClassName) {
16676                 cell.initialClassName = cell.dom.className;
16677             }
16678             
16679             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16680         };
16681
16682         var i = 0;
16683         
16684         for(; i < startingPos; i++) {
16685             textEls[i].innerHTML = (++prevStart);
16686             d.setDate(d.getDate()+1);
16687             
16688             cells[i].className = "fc-past fc-other-month";
16689             setCellClass(this, cells[i]);
16690         }
16691         
16692         var intDay = 0;
16693         
16694         for(; i < days; i++){
16695             intDay = i - startingPos + 1;
16696             textEls[i].innerHTML = (intDay);
16697             d.setDate(d.getDate()+1);
16698             
16699             cells[i].className = ''; // "x-date-active";
16700             setCellClass(this, cells[i]);
16701         }
16702         var extraDays = 0;
16703         
16704         for(; i < 42; i++) {
16705             textEls[i].innerHTML = (++extraDays);
16706             d.setDate(d.getDate()+1);
16707             
16708             cells[i].className = "fc-future fc-other-month";
16709             setCellClass(this, cells[i]);
16710         }
16711         
16712         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16713         
16714         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16715         
16716         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16717         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16718         
16719         if(totalRows != 6){
16720             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16721             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16722         }
16723         
16724         this.fireEvent('monthchange', this, date);
16725         
16726         
16727         /*
16728         if(!this.internalRender){
16729             var main = this.el.dom.firstChild;
16730             var w = main.offsetWidth;
16731             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16732             Roo.fly(main).setWidth(w);
16733             this.internalRender = true;
16734             // opera does not respect the auto grow header center column
16735             // then, after it gets a width opera refuses to recalculate
16736             // without a second pass
16737             if(Roo.isOpera && !this.secondPass){
16738                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16739                 this.secondPass = true;
16740                 this.update.defer(10, this, [date]);
16741             }
16742         }
16743         */
16744         
16745     },
16746     
16747     findCell : function(dt) {
16748         dt = dt.clearTime().getTime();
16749         var ret = false;
16750         this.cells.each(function(c){
16751             //Roo.log("check " +c.dateValue + '?=' + dt);
16752             if(c.dateValue == dt){
16753                 ret = c;
16754                 return false;
16755             }
16756             return true;
16757         });
16758         
16759         return ret;
16760     },
16761     
16762     findCells : function(ev) {
16763         var s = ev.start.clone().clearTime().getTime();
16764        // Roo.log(s);
16765         var e= ev.end.clone().clearTime().getTime();
16766        // Roo.log(e);
16767         var ret = [];
16768         this.cells.each(function(c){
16769              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16770             
16771             if(c.dateValue > e){
16772                 return ;
16773             }
16774             if(c.dateValue < s){
16775                 return ;
16776             }
16777             ret.push(c);
16778         });
16779         
16780         return ret;    
16781     },
16782     
16783 //    findBestRow: function(cells)
16784 //    {
16785 //        var ret = 0;
16786 //        
16787 //        for (var i =0 ; i < cells.length;i++) {
16788 //            ret  = Math.max(cells[i].rows || 0,ret);
16789 //        }
16790 //        return ret;
16791 //        
16792 //    },
16793     
16794     
16795     addItem : function(ev)
16796     {
16797         // look for vertical location slot in
16798         var cells = this.findCells(ev);
16799         
16800 //        ev.row = this.findBestRow(cells);
16801         
16802         // work out the location.
16803         
16804         var crow = false;
16805         var rows = [];
16806         for(var i =0; i < cells.length; i++) {
16807             
16808             cells[i].row = cells[0].row;
16809             
16810             if(i == 0){
16811                 cells[i].row = cells[i].row + 1;
16812             }
16813             
16814             if (!crow) {
16815                 crow = {
16816                     start : cells[i],
16817                     end :  cells[i]
16818                 };
16819                 continue;
16820             }
16821             if (crow.start.getY() == cells[i].getY()) {
16822                 // on same row.
16823                 crow.end = cells[i];
16824                 continue;
16825             }
16826             // different row.
16827             rows.push(crow);
16828             crow = {
16829                 start: cells[i],
16830                 end : cells[i]
16831             };
16832             
16833         }
16834         
16835         rows.push(crow);
16836         ev.els = [];
16837         ev.rows = rows;
16838         ev.cells = cells;
16839         
16840         cells[0].events.push(ev);
16841         
16842         this.calevents.push(ev);
16843     },
16844     
16845     clearEvents: function() {
16846         
16847         if(!this.calevents){
16848             return;
16849         }
16850         
16851         Roo.each(this.cells.elements, function(c){
16852             c.row = 0;
16853             c.events = [];
16854             c.more = [];
16855         });
16856         
16857         Roo.each(this.calevents, function(e) {
16858             Roo.each(e.els, function(el) {
16859                 el.un('mouseenter' ,this.onEventEnter, this);
16860                 el.un('mouseleave' ,this.onEventLeave, this);
16861                 el.remove();
16862             },this);
16863         },this);
16864         
16865         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16866             e.remove();
16867         });
16868         
16869     },
16870     
16871     renderEvents: function()
16872     {   
16873         var _this = this;
16874         
16875         this.cells.each(function(c) {
16876             
16877             if(c.row < 5){
16878                 return;
16879             }
16880             
16881             var ev = c.events;
16882             
16883             var r = 4;
16884             if(c.row != c.events.length){
16885                 r = 4 - (4 - (c.row - c.events.length));
16886             }
16887             
16888             c.events = ev.slice(0, r);
16889             c.more = ev.slice(r);
16890             
16891             if(c.more.length && c.more.length == 1){
16892                 c.events.push(c.more.pop());
16893             }
16894             
16895             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16896             
16897         });
16898             
16899         this.cells.each(function(c) {
16900             
16901             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16902             
16903             
16904             for (var e = 0; e < c.events.length; e++){
16905                 var ev = c.events[e];
16906                 var rows = ev.rows;
16907                 
16908                 for(var i = 0; i < rows.length; i++) {
16909                 
16910                     // how many rows should it span..
16911
16912                     var  cfg = {
16913                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16914                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16915
16916                         unselectable : "on",
16917                         cn : [
16918                             {
16919                                 cls: 'fc-event-inner',
16920                                 cn : [
16921     //                                {
16922     //                                  tag:'span',
16923     //                                  cls: 'fc-event-time',
16924     //                                  html : cells.length > 1 ? '' : ev.time
16925     //                                },
16926                                     {
16927                                       tag:'span',
16928                                       cls: 'fc-event-title',
16929                                       html : String.format('{0}', ev.title)
16930                                     }
16931
16932
16933                                 ]
16934                             },
16935                             {
16936                                 cls: 'ui-resizable-handle ui-resizable-e',
16937                                 html : '&nbsp;&nbsp;&nbsp'
16938                             }
16939
16940                         ]
16941                     };
16942
16943                     if (i == 0) {
16944                         cfg.cls += ' fc-event-start';
16945                     }
16946                     if ((i+1) == rows.length) {
16947                         cfg.cls += ' fc-event-end';
16948                     }
16949
16950                     var ctr = _this.el.select('.fc-event-container',true).first();
16951                     var cg = ctr.createChild(cfg);
16952
16953                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16954                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16955
16956                     var r = (c.more.length) ? 1 : 0;
16957                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16958                     cg.setWidth(ebox.right - sbox.x -2);
16959
16960                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16961                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16962                     cg.on('click', _this.onEventClick, _this, ev);
16963
16964                     ev.els.push(cg);
16965                     
16966                 }
16967                 
16968             }
16969             
16970             
16971             if(c.more.length){
16972                 var  cfg = {
16973                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16974                     style : 'position: absolute',
16975                     unselectable : "on",
16976                     cn : [
16977                         {
16978                             cls: 'fc-event-inner',
16979                             cn : [
16980                                 {
16981                                   tag:'span',
16982                                   cls: 'fc-event-title',
16983                                   html : 'More'
16984                                 }
16985
16986
16987                             ]
16988                         },
16989                         {
16990                             cls: 'ui-resizable-handle ui-resizable-e',
16991                             html : '&nbsp;&nbsp;&nbsp'
16992                         }
16993
16994                     ]
16995                 };
16996
16997                 var ctr = _this.el.select('.fc-event-container',true).first();
16998                 var cg = ctr.createChild(cfg);
16999
17000                 var sbox = c.select('.fc-day-content',true).first().getBox();
17001                 var ebox = c.select('.fc-day-content',true).first().getBox();
17002                 //Roo.log(cg);
17003                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17004                 cg.setWidth(ebox.right - sbox.x -2);
17005
17006                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17007                 
17008             }
17009             
17010         });
17011         
17012         
17013         
17014     },
17015     
17016     onEventEnter: function (e, el,event,d) {
17017         this.fireEvent('evententer', this, el, event);
17018     },
17019     
17020     onEventLeave: function (e, el,event,d) {
17021         this.fireEvent('eventleave', this, el, event);
17022     },
17023     
17024     onEventClick: function (e, el,event,d) {
17025         this.fireEvent('eventclick', this, el, event);
17026     },
17027     
17028     onMonthChange: function () {
17029         this.store.load();
17030     },
17031     
17032     onMoreEventClick: function(e, el, more)
17033     {
17034         var _this = this;
17035         
17036         this.calpopover.placement = 'right';
17037         this.calpopover.setTitle('More');
17038         
17039         this.calpopover.setContent('');
17040         
17041         var ctr = this.calpopover.el.select('.popover-content', true).first();
17042         
17043         Roo.each(more, function(m){
17044             var cfg = {
17045                 cls : 'fc-event-hori fc-event-draggable',
17046                 html : m.title
17047             };
17048             var cg = ctr.createChild(cfg);
17049             
17050             cg.on('click', _this.onEventClick, _this, m);
17051         });
17052         
17053         this.calpopover.show(el);
17054         
17055         
17056     },
17057     
17058     onLoad: function () 
17059     {   
17060         this.calevents = [];
17061         var cal = this;
17062         
17063         if(this.store.getCount() > 0){
17064             this.store.data.each(function(d){
17065                cal.addItem({
17066                     id : d.data.id,
17067                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17068                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17069                     time : d.data.start_time,
17070                     title : d.data.title,
17071                     description : d.data.description,
17072                     venue : d.data.venue
17073                 });
17074             });
17075         }
17076         
17077         this.renderEvents();
17078         
17079         if(this.calevents.length && this.loadMask){
17080             this.maskEl.hide();
17081         }
17082     },
17083     
17084     onBeforeLoad: function()
17085     {
17086         this.clearEvents();
17087         if(this.loadMask){
17088             this.maskEl.show();
17089         }
17090     }
17091 });
17092
17093  
17094  /*
17095  * - LGPL
17096  *
17097  * element
17098  * 
17099  */
17100
17101 /**
17102  * @class Roo.bootstrap.Popover
17103  * @extends Roo.bootstrap.Component
17104  * Bootstrap Popover class
17105  * @cfg {String} html contents of the popover   (or false to use children..)
17106  * @cfg {String} title of popover (or false to hide)
17107  * @cfg {String} placement how it is placed
17108  * @cfg {String} trigger click || hover (or false to trigger manually)
17109  * @cfg {String} over what (parent or false to trigger manually.)
17110  * @cfg {Number} delay - delay before showing
17111  
17112  * @constructor
17113  * Create a new Popover
17114  * @param {Object} config The config object
17115  */
17116
17117 Roo.bootstrap.Popover = function(config){
17118     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17119     
17120     this.addEvents({
17121         // raw events
17122          /**
17123          * @event show
17124          * After the popover show
17125          * 
17126          * @param {Roo.bootstrap.Popover} this
17127          */
17128         "show" : true,
17129         /**
17130          * @event hide
17131          * After the popover hide
17132          * 
17133          * @param {Roo.bootstrap.Popover} this
17134          */
17135         "hide" : true
17136     });
17137 };
17138
17139 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17140     
17141     title: 'Fill in a title',
17142     html: false,
17143     
17144     placement : 'right',
17145     trigger : 'hover', // hover
17146     
17147     delay : 0,
17148     
17149     over: 'parent',
17150     
17151     can_build_overlaid : false,
17152     
17153     getChildContainer : function()
17154     {
17155         return this.el.select('.popover-content',true).first();
17156     },
17157     
17158     getAutoCreate : function(){
17159          
17160         var cfg = {
17161            cls : 'popover roo-dynamic',
17162            style: 'display:block',
17163            cn : [
17164                 {
17165                     cls : 'arrow'
17166                 },
17167                 {
17168                     cls : 'popover-inner',
17169                     cn : [
17170                         {
17171                             tag: 'h3',
17172                             cls: 'popover-title',
17173                             html : this.title
17174                         },
17175                         {
17176                             cls : 'popover-content',
17177                             html : this.html
17178                         }
17179                     ]
17180                     
17181                 }
17182            ]
17183         };
17184         
17185         return cfg;
17186     },
17187     setTitle: function(str)
17188     {
17189         this.title = str;
17190         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17191     },
17192     setContent: function(str)
17193     {
17194         this.html = str;
17195         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17196     },
17197     // as it get's added to the bottom of the page.
17198     onRender : function(ct, position)
17199     {
17200         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17201         if(!this.el){
17202             var cfg = Roo.apply({},  this.getAutoCreate());
17203             cfg.id = Roo.id();
17204             
17205             if (this.cls) {
17206                 cfg.cls += ' ' + this.cls;
17207             }
17208             if (this.style) {
17209                 cfg.style = this.style;
17210             }
17211             //Roo.log("adding to ");
17212             this.el = Roo.get(document.body).createChild(cfg, position);
17213 //            Roo.log(this.el);
17214         }
17215         this.initEvents();
17216     },
17217     
17218     initEvents : function()
17219     {
17220         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17221         this.el.enableDisplayMode('block');
17222         this.el.hide();
17223         if (this.over === false) {
17224             return; 
17225         }
17226         if (this.triggers === false) {
17227             return;
17228         }
17229         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17230         var triggers = this.trigger ? this.trigger.split(' ') : [];
17231         Roo.each(triggers, function(trigger) {
17232         
17233             if (trigger == 'click') {
17234                 on_el.on('click', this.toggle, this);
17235             } else if (trigger != 'manual') {
17236                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17237                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17238       
17239                 on_el.on(eventIn  ,this.enter, this);
17240                 on_el.on(eventOut, this.leave, this);
17241             }
17242         }, this);
17243         
17244     },
17245     
17246     
17247     // private
17248     timeout : null,
17249     hoverState : null,
17250     
17251     toggle : function () {
17252         this.hoverState == 'in' ? this.leave() : this.enter();
17253     },
17254     
17255     enter : function () {
17256         
17257         clearTimeout(this.timeout);
17258     
17259         this.hoverState = 'in';
17260     
17261         if (!this.delay || !this.delay.show) {
17262             this.show();
17263             return;
17264         }
17265         var _t = this;
17266         this.timeout = setTimeout(function () {
17267             if (_t.hoverState == 'in') {
17268                 _t.show();
17269             }
17270         }, this.delay.show)
17271     },
17272     
17273     leave : function() {
17274         clearTimeout(this.timeout);
17275     
17276         this.hoverState = 'out';
17277     
17278         if (!this.delay || !this.delay.hide) {
17279             this.hide();
17280             return;
17281         }
17282         var _t = this;
17283         this.timeout = setTimeout(function () {
17284             if (_t.hoverState == 'out') {
17285                 _t.hide();
17286             }
17287         }, this.delay.hide)
17288     },
17289     
17290     show : function (on_el)
17291     {
17292         if (!on_el) {
17293             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17294         }
17295         
17296         // set content.
17297         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17298         if (this.html !== false) {
17299             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17300         }
17301         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17302         if (!this.title.length) {
17303             this.el.select('.popover-title',true).hide();
17304         }
17305         
17306         var placement = typeof this.placement == 'function' ?
17307             this.placement.call(this, this.el, on_el) :
17308             this.placement;
17309             
17310         var autoToken = /\s?auto?\s?/i;
17311         var autoPlace = autoToken.test(placement);
17312         if (autoPlace) {
17313             placement = placement.replace(autoToken, '') || 'top';
17314         }
17315         
17316         //this.el.detach()
17317         //this.el.setXY([0,0]);
17318         this.el.show();
17319         this.el.dom.style.display='block';
17320         this.el.addClass(placement);
17321         
17322         //this.el.appendTo(on_el);
17323         
17324         var p = this.getPosition();
17325         var box = this.el.getBox();
17326         
17327         if (autoPlace) {
17328             // fixme..
17329         }
17330         var align = Roo.bootstrap.Popover.alignment[placement];
17331         this.el.alignTo(on_el, align[0],align[1]);
17332         //var arrow = this.el.select('.arrow',true).first();
17333         //arrow.set(align[2], 
17334         
17335         this.el.addClass('in');
17336         
17337         
17338         if (this.el.hasClass('fade')) {
17339             // fade it?
17340         }
17341         
17342         this.hoverState = 'in';
17343         
17344         this.fireEvent('show', this);
17345         
17346     },
17347     hide : function()
17348     {
17349         this.el.setXY([0,0]);
17350         this.el.removeClass('in');
17351         this.el.hide();
17352         this.hoverState = null;
17353         
17354         this.fireEvent('hide', this);
17355     }
17356     
17357 });
17358
17359 Roo.bootstrap.Popover.alignment = {
17360     'left' : ['r-l', [-10,0], 'right'],
17361     'right' : ['l-r', [10,0], 'left'],
17362     'bottom' : ['t-b', [0,10], 'top'],
17363     'top' : [ 'b-t', [0,-10], 'bottom']
17364 };
17365
17366  /*
17367  * - LGPL
17368  *
17369  * Progress
17370  * 
17371  */
17372
17373 /**
17374  * @class Roo.bootstrap.Progress
17375  * @extends Roo.bootstrap.Component
17376  * Bootstrap Progress class
17377  * @cfg {Boolean} striped striped of the progress bar
17378  * @cfg {Boolean} active animated of the progress bar
17379  * 
17380  * 
17381  * @constructor
17382  * Create a new Progress
17383  * @param {Object} config The config object
17384  */
17385
17386 Roo.bootstrap.Progress = function(config){
17387     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17388 };
17389
17390 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17391     
17392     striped : false,
17393     active: false,
17394     
17395     getAutoCreate : function(){
17396         var cfg = {
17397             tag: 'div',
17398             cls: 'progress'
17399         };
17400         
17401         
17402         if(this.striped){
17403             cfg.cls += ' progress-striped';
17404         }
17405       
17406         if(this.active){
17407             cfg.cls += ' active';
17408         }
17409         
17410         
17411         return cfg;
17412     }
17413    
17414 });
17415
17416  
17417
17418  /*
17419  * - LGPL
17420  *
17421  * ProgressBar
17422  * 
17423  */
17424
17425 /**
17426  * @class Roo.bootstrap.ProgressBar
17427  * @extends Roo.bootstrap.Component
17428  * Bootstrap ProgressBar class
17429  * @cfg {Number} aria_valuenow aria-value now
17430  * @cfg {Number} aria_valuemin aria-value min
17431  * @cfg {Number} aria_valuemax aria-value max
17432  * @cfg {String} label label for the progress bar
17433  * @cfg {String} panel (success | info | warning | danger )
17434  * @cfg {String} role role of the progress bar
17435  * @cfg {String} sr_only text
17436  * 
17437  * 
17438  * @constructor
17439  * Create a new ProgressBar
17440  * @param {Object} config The config object
17441  */
17442
17443 Roo.bootstrap.ProgressBar = function(config){
17444     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17445 };
17446
17447 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17448     
17449     aria_valuenow : 0,
17450     aria_valuemin : 0,
17451     aria_valuemax : 100,
17452     label : false,
17453     panel : false,
17454     role : false,
17455     sr_only: false,
17456     
17457     getAutoCreate : function()
17458     {
17459         
17460         var cfg = {
17461             tag: 'div',
17462             cls: 'progress-bar',
17463             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17464         };
17465         
17466         if(this.sr_only){
17467             cfg.cn = {
17468                 tag: 'span',
17469                 cls: 'sr-only',
17470                 html: this.sr_only
17471             }
17472         }
17473         
17474         if(this.role){
17475             cfg.role = this.role;
17476         }
17477         
17478         if(this.aria_valuenow){
17479             cfg['aria-valuenow'] = this.aria_valuenow;
17480         }
17481         
17482         if(this.aria_valuemin){
17483             cfg['aria-valuemin'] = this.aria_valuemin;
17484         }
17485         
17486         if(this.aria_valuemax){
17487             cfg['aria-valuemax'] = this.aria_valuemax;
17488         }
17489         
17490         if(this.label && !this.sr_only){
17491             cfg.html = this.label;
17492         }
17493         
17494         if(this.panel){
17495             cfg.cls += ' progress-bar-' + this.panel;
17496         }
17497         
17498         return cfg;
17499     },
17500     
17501     update : function(aria_valuenow)
17502     {
17503         this.aria_valuenow = aria_valuenow;
17504         
17505         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17506     }
17507    
17508 });
17509
17510  
17511
17512  /*
17513  * - LGPL
17514  *
17515  * column
17516  * 
17517  */
17518
17519 /**
17520  * @class Roo.bootstrap.TabGroup
17521  * @extends Roo.bootstrap.Column
17522  * Bootstrap Column class
17523  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17524  * @cfg {Boolean} carousel true to make the group behave like a carousel
17525  * @cfg {Boolean} bullets show bullets for the panels
17526  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17527  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17528  * @cfg {Boolean} showarrow (true|false) show arrow default true
17529  * 
17530  * @constructor
17531  * Create a new TabGroup
17532  * @param {Object} config The config object
17533  */
17534
17535 Roo.bootstrap.TabGroup = function(config){
17536     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17537     if (!this.navId) {
17538         this.navId = Roo.id();
17539     }
17540     this.tabs = [];
17541     Roo.bootstrap.TabGroup.register(this);
17542     
17543 };
17544
17545 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17546     
17547     carousel : false,
17548     transition : false,
17549     bullets : 0,
17550     timer : 0,
17551     autoslide : false,
17552     slideFn : false,
17553     slideOnTouch : false,
17554     showarrow : true,
17555     
17556     getAutoCreate : function()
17557     {
17558         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17559         
17560         cfg.cls += ' tab-content';
17561         
17562         if (this.carousel) {
17563             cfg.cls += ' carousel slide';
17564             
17565             cfg.cn = [{
17566                cls : 'carousel-inner',
17567                cn : []
17568             }];
17569         
17570             if(this.bullets  && !Roo.isTouch){
17571                 
17572                 var bullets = {
17573                     cls : 'carousel-bullets',
17574                     cn : []
17575                 };
17576                
17577                 if(this.bullets_cls){
17578                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17579                 }
17580                 
17581                 bullets.cn.push({
17582                     cls : 'clear'
17583                 });
17584                 
17585                 cfg.cn[0].cn.push(bullets);
17586             }
17587             
17588             if(this.showarrow){
17589                 cfg.cn[0].cn.push({
17590                     tag : 'div',
17591                     class : 'carousel-arrow',
17592                     cn : [
17593                         {
17594                             tag : 'div',
17595                             class : 'carousel-prev',
17596                             cn : [
17597                                 {
17598                                     tag : 'i',
17599                                     class : 'fa fa-chevron-left'
17600                                 }
17601                             ]
17602                         },
17603                         {
17604                             tag : 'div',
17605                             class : 'carousel-next',
17606                             cn : [
17607                                 {
17608                                     tag : 'i',
17609                                     class : 'fa fa-chevron-right'
17610                                 }
17611                             ]
17612                         }
17613                     ]
17614                 });
17615             }
17616             
17617         }
17618         
17619         return cfg;
17620     },
17621     
17622     initEvents:  function()
17623     {
17624 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17625 //            this.el.on("touchstart", this.onTouchStart, this);
17626 //        }
17627         
17628         if(this.autoslide){
17629             var _this = this;
17630             
17631             this.slideFn = window.setInterval(function() {
17632                 _this.showPanelNext();
17633             }, this.timer);
17634         }
17635         
17636         if(this.showarrow){
17637             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17638             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17639         }
17640         
17641         
17642     },
17643     
17644 //    onTouchStart : function(e, el, o)
17645 //    {
17646 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17647 //            return;
17648 //        }
17649 //        
17650 //        this.showPanelNext();
17651 //    },
17652     
17653     
17654     getChildContainer : function()
17655     {
17656         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17657     },
17658     
17659     /**
17660     * register a Navigation item
17661     * @param {Roo.bootstrap.NavItem} the navitem to add
17662     */
17663     register : function(item)
17664     {
17665         this.tabs.push( item);
17666         item.navId = this.navId; // not really needed..
17667         this.addBullet();
17668     
17669     },
17670     
17671     getActivePanel : function()
17672     {
17673         var r = false;
17674         Roo.each(this.tabs, function(t) {
17675             if (t.active) {
17676                 r = t;
17677                 return false;
17678             }
17679             return null;
17680         });
17681         return r;
17682         
17683     },
17684     getPanelByName : function(n)
17685     {
17686         var r = false;
17687         Roo.each(this.tabs, function(t) {
17688             if (t.tabId == n) {
17689                 r = t;
17690                 return false;
17691             }
17692             return null;
17693         });
17694         return r;
17695     },
17696     indexOfPanel : function(p)
17697     {
17698         var r = false;
17699         Roo.each(this.tabs, function(t,i) {
17700             if (t.tabId == p.tabId) {
17701                 r = i;
17702                 return false;
17703             }
17704             return null;
17705         });
17706         return r;
17707     },
17708     /**
17709      * show a specific panel
17710      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17711      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17712      */
17713     showPanel : function (pan)
17714     {
17715         if(this.transition || typeof(pan) == 'undefined'){
17716             Roo.log("waiting for the transitionend");
17717             return;
17718         }
17719         
17720         if (typeof(pan) == 'number') {
17721             pan = this.tabs[pan];
17722         }
17723         
17724         if (typeof(pan) == 'string') {
17725             pan = this.getPanelByName(pan);
17726         }
17727         
17728         var cur = this.getActivePanel();
17729         
17730         if(!pan || !cur){
17731             Roo.log('pan or acitve pan is undefined');
17732             return false;
17733         }
17734         
17735         if (pan.tabId == this.getActivePanel().tabId) {
17736             return true;
17737         }
17738         
17739         if (false === cur.fireEvent('beforedeactivate')) {
17740             return false;
17741         }
17742         
17743         if(this.bullets > 0 && !Roo.isTouch){
17744             this.setActiveBullet(this.indexOfPanel(pan));
17745         }
17746         
17747         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17748             
17749             this.transition = true;
17750             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17751             var lr = dir == 'next' ? 'left' : 'right';
17752             pan.el.addClass(dir); // or prev
17753             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17754             cur.el.addClass(lr); // or right
17755             pan.el.addClass(lr);
17756             
17757             var _this = this;
17758             cur.el.on('transitionend', function() {
17759                 Roo.log("trans end?");
17760                 
17761                 pan.el.removeClass([lr,dir]);
17762                 pan.setActive(true);
17763                 
17764                 cur.el.removeClass([lr]);
17765                 cur.setActive(false);
17766                 
17767                 _this.transition = false;
17768                 
17769             }, this, { single:  true } );
17770             
17771             return true;
17772         }
17773         
17774         cur.setActive(false);
17775         pan.setActive(true);
17776         
17777         return true;
17778         
17779     },
17780     showPanelNext : function()
17781     {
17782         var i = this.indexOfPanel(this.getActivePanel());
17783         
17784         if (i >= this.tabs.length - 1 && !this.autoslide) {
17785             return;
17786         }
17787         
17788         if (i >= this.tabs.length - 1 && this.autoslide) {
17789             i = -1;
17790         }
17791         
17792         this.showPanel(this.tabs[i+1]);
17793     },
17794     
17795     showPanelPrev : function()
17796     {
17797         var i = this.indexOfPanel(this.getActivePanel());
17798         
17799         if (i  < 1 && !this.autoslide) {
17800             return;
17801         }
17802         
17803         if (i < 1 && this.autoslide) {
17804             i = this.tabs.length;
17805         }
17806         
17807         this.showPanel(this.tabs[i-1]);
17808     },
17809     
17810     
17811     addBullet: function()
17812     {
17813         if(!this.bullets || Roo.isTouch){
17814             return;
17815         }
17816         var ctr = this.el.select('.carousel-bullets',true).first();
17817         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17818         var bullet = ctr.createChild({
17819             cls : 'bullet bullet-' + i
17820         },ctr.dom.lastChild);
17821         
17822         
17823         var _this = this;
17824         
17825         bullet.on('click', (function(e, el, o, ii, t){
17826
17827             e.preventDefault();
17828
17829             this.showPanel(ii);
17830
17831             if(this.autoslide && this.slideFn){
17832                 clearInterval(this.slideFn);
17833                 this.slideFn = window.setInterval(function() {
17834                     _this.showPanelNext();
17835                 }, this.timer);
17836             }
17837
17838         }).createDelegate(this, [i, bullet], true));
17839                 
17840         
17841     },
17842      
17843     setActiveBullet : function(i)
17844     {
17845         if(Roo.isTouch){
17846             return;
17847         }
17848         
17849         Roo.each(this.el.select('.bullet', true).elements, function(el){
17850             el.removeClass('selected');
17851         });
17852
17853         var bullet = this.el.select('.bullet-' + i, true).first();
17854         
17855         if(!bullet){
17856             return;
17857         }
17858         
17859         bullet.addClass('selected');
17860     }
17861     
17862     
17863   
17864 });
17865
17866  
17867
17868  
17869  
17870 Roo.apply(Roo.bootstrap.TabGroup, {
17871     
17872     groups: {},
17873      /**
17874     * register a Navigation Group
17875     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17876     */
17877     register : function(navgrp)
17878     {
17879         this.groups[navgrp.navId] = navgrp;
17880         
17881     },
17882     /**
17883     * fetch a Navigation Group based on the navigation ID
17884     * if one does not exist , it will get created.
17885     * @param {string} the navgroup to add
17886     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17887     */
17888     get: function(navId) {
17889         if (typeof(this.groups[navId]) == 'undefined') {
17890             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17891         }
17892         return this.groups[navId] ;
17893     }
17894     
17895     
17896     
17897 });
17898
17899  /*
17900  * - LGPL
17901  *
17902  * TabPanel
17903  * 
17904  */
17905
17906 /**
17907  * @class Roo.bootstrap.TabPanel
17908  * @extends Roo.bootstrap.Component
17909  * Bootstrap TabPanel class
17910  * @cfg {Boolean} active panel active
17911  * @cfg {String} html panel content
17912  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17913  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17914  * @cfg {String} href click to link..
17915  * 
17916  * 
17917  * @constructor
17918  * Create a new TabPanel
17919  * @param {Object} config The config object
17920  */
17921
17922 Roo.bootstrap.TabPanel = function(config){
17923     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17924     this.addEvents({
17925         /**
17926              * @event changed
17927              * Fires when the active status changes
17928              * @param {Roo.bootstrap.TabPanel} this
17929              * @param {Boolean} state the new state
17930             
17931          */
17932         'changed': true,
17933         /**
17934              * @event beforedeactivate
17935              * Fires before a tab is de-activated - can be used to do validation on a form.
17936              * @param {Roo.bootstrap.TabPanel} this
17937              * @return {Boolean} false if there is an error
17938             
17939          */
17940         'beforedeactivate': true
17941      });
17942     
17943     this.tabId = this.tabId || Roo.id();
17944   
17945 };
17946
17947 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17948     
17949     active: false,
17950     html: false,
17951     tabId: false,
17952     navId : false,
17953     href : '',
17954     
17955     getAutoCreate : function(){
17956         var cfg = {
17957             tag: 'div',
17958             // item is needed for carousel - not sure if it has any effect otherwise
17959             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17960             html: this.html || ''
17961         };
17962         
17963         if(this.active){
17964             cfg.cls += ' active';
17965         }
17966         
17967         if(this.tabId){
17968             cfg.tabId = this.tabId;
17969         }
17970         
17971         
17972         return cfg;
17973     },
17974     
17975     initEvents:  function()
17976     {
17977         var p = this.parent();
17978         
17979         this.navId = this.navId || p.navId;
17980         
17981         if (typeof(this.navId) != 'undefined') {
17982             // not really needed.. but just in case.. parent should be a NavGroup.
17983             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17984             
17985             tg.register(this);
17986             
17987             var i = tg.tabs.length - 1;
17988             
17989             if(this.active && tg.bullets > 0 && i < tg.bullets){
17990                 tg.setActiveBullet(i);
17991             }
17992         }
17993         
17994         this.el.on('click', this.onClick, this);
17995         
17996         if(Roo.isTouch){
17997             this.el.on("touchstart", this.onTouchStart, this);
17998             this.el.on("touchmove", this.onTouchMove, this);
17999             this.el.on("touchend", this.onTouchEnd, this);
18000         }
18001         
18002     },
18003     
18004     onRender : function(ct, position)
18005     {
18006         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18007     },
18008     
18009     setActive : function(state)
18010     {
18011         Roo.log("panel - set active " + this.tabId + "=" + state);
18012         
18013         this.active = state;
18014         if (!state) {
18015             this.el.removeClass('active');
18016             
18017         } else  if (!this.el.hasClass('active')) {
18018             this.el.addClass('active');
18019         }
18020         
18021         this.fireEvent('changed', this, state);
18022     },
18023     
18024     onClick : function(e)
18025     {
18026         e.preventDefault();
18027         
18028         if(!this.href.length){
18029             return;
18030         }
18031         
18032         window.location.href = this.href;
18033     },
18034     
18035     startX : 0,
18036     startY : 0,
18037     endX : 0,
18038     endY : 0,
18039     swiping : false,
18040     
18041     onTouchStart : function(e)
18042     {
18043         this.swiping = false;
18044         
18045         this.startX = e.browserEvent.touches[0].clientX;
18046         this.startY = e.browserEvent.touches[0].clientY;
18047     },
18048     
18049     onTouchMove : function(e)
18050     {
18051         this.swiping = true;
18052         
18053         this.endX = e.browserEvent.touches[0].clientX;
18054         this.endY = e.browserEvent.touches[0].clientY;
18055     },
18056     
18057     onTouchEnd : function(e)
18058     {
18059         if(!this.swiping){
18060             this.onClick(e);
18061             return;
18062         }
18063         
18064         var tabGroup = this.parent();
18065         
18066         if(this.endX > this.startX){ // swiping right
18067             tabGroup.showPanelPrev();
18068             return;
18069         }
18070         
18071         if(this.startX > this.endX){ // swiping left
18072             tabGroup.showPanelNext();
18073             return;
18074         }
18075     }
18076     
18077     
18078 });
18079  
18080
18081  
18082
18083  /*
18084  * - LGPL
18085  *
18086  * DateField
18087  * 
18088  */
18089
18090 /**
18091  * @class Roo.bootstrap.DateField
18092  * @extends Roo.bootstrap.Input
18093  * Bootstrap DateField class
18094  * @cfg {Number} weekStart default 0
18095  * @cfg {String} viewMode default empty, (months|years)
18096  * @cfg {String} minViewMode default empty, (months|years)
18097  * @cfg {Number} startDate default -Infinity
18098  * @cfg {Number} endDate default Infinity
18099  * @cfg {Boolean} todayHighlight default false
18100  * @cfg {Boolean} todayBtn default false
18101  * @cfg {Boolean} calendarWeeks default false
18102  * @cfg {Object} daysOfWeekDisabled default empty
18103  * @cfg {Boolean} singleMode default false (true | false)
18104  * 
18105  * @cfg {Boolean} keyboardNavigation default true
18106  * @cfg {String} language default en
18107  * 
18108  * @constructor
18109  * Create a new DateField
18110  * @param {Object} config The config object
18111  */
18112
18113 Roo.bootstrap.DateField = function(config){
18114     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18115      this.addEvents({
18116             /**
18117              * @event show
18118              * Fires when this field show.
18119              * @param {Roo.bootstrap.DateField} this
18120              * @param {Mixed} date The date value
18121              */
18122             show : true,
18123             /**
18124              * @event show
18125              * Fires when this field hide.
18126              * @param {Roo.bootstrap.DateField} this
18127              * @param {Mixed} date The date value
18128              */
18129             hide : true,
18130             /**
18131              * @event select
18132              * Fires when select a date.
18133              * @param {Roo.bootstrap.DateField} this
18134              * @param {Mixed} date The date value
18135              */
18136             select : true,
18137             /**
18138              * @event beforeselect
18139              * Fires when before select a date.
18140              * @param {Roo.bootstrap.DateField} this
18141              * @param {Mixed} date The date value
18142              */
18143             beforeselect : true
18144         });
18145 };
18146
18147 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18148     
18149     /**
18150      * @cfg {String} format
18151      * The default date format string which can be overriden for localization support.  The format must be
18152      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18153      */
18154     format : "m/d/y",
18155     /**
18156      * @cfg {String} altFormats
18157      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18158      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18159      */
18160     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18161     
18162     weekStart : 0,
18163     
18164     viewMode : '',
18165     
18166     minViewMode : '',
18167     
18168     todayHighlight : false,
18169     
18170     todayBtn: false,
18171     
18172     language: 'en',
18173     
18174     keyboardNavigation: true,
18175     
18176     calendarWeeks: false,
18177     
18178     startDate: -Infinity,
18179     
18180     endDate: Infinity,
18181     
18182     daysOfWeekDisabled: [],
18183     
18184     _events: [],
18185     
18186     singleMode : false,
18187     
18188     UTCDate: function()
18189     {
18190         return new Date(Date.UTC.apply(Date, arguments));
18191     },
18192     
18193     UTCToday: function()
18194     {
18195         var today = new Date();
18196         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18197     },
18198     
18199     getDate: function() {
18200             var d = this.getUTCDate();
18201             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18202     },
18203     
18204     getUTCDate: function() {
18205             return this.date;
18206     },
18207     
18208     setDate: function(d) {
18209             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18210     },
18211     
18212     setUTCDate: function(d) {
18213             this.date = d;
18214             this.setValue(this.formatDate(this.date));
18215     },
18216         
18217     onRender: function(ct, position)
18218     {
18219         
18220         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18221         
18222         this.language = this.language || 'en';
18223         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18224         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18225         
18226         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18227         this.format = this.format || 'm/d/y';
18228         this.isInline = false;
18229         this.isInput = true;
18230         this.component = this.el.select('.add-on', true).first() || false;
18231         this.component = (this.component && this.component.length === 0) ? false : this.component;
18232         this.hasInput = this.component && this.inputEl().length;
18233         
18234         if (typeof(this.minViewMode === 'string')) {
18235             switch (this.minViewMode) {
18236                 case 'months':
18237                     this.minViewMode = 1;
18238                     break;
18239                 case 'years':
18240                     this.minViewMode = 2;
18241                     break;
18242                 default:
18243                     this.minViewMode = 0;
18244                     break;
18245             }
18246         }
18247         
18248         if (typeof(this.viewMode === 'string')) {
18249             switch (this.viewMode) {
18250                 case 'months':
18251                     this.viewMode = 1;
18252                     break;
18253                 case 'years':
18254                     this.viewMode = 2;
18255                     break;
18256                 default:
18257                     this.viewMode = 0;
18258                     break;
18259             }
18260         }
18261                 
18262         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18263         
18264 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18265         
18266         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18267         
18268         this.picker().on('mousedown', this.onMousedown, this);
18269         this.picker().on('click', this.onClick, this);
18270         
18271         this.picker().addClass('datepicker-dropdown');
18272         
18273         this.startViewMode = this.viewMode;
18274         
18275         if(this.singleMode){
18276             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18277                 v.setVisibilityMode(Roo.Element.DISPLAY);
18278                 v.hide();
18279             });
18280             
18281             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18282                 v.setStyle('width', '189px');
18283             });
18284         }
18285         
18286         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18287             if(!this.calendarWeeks){
18288                 v.remove();
18289                 return;
18290             }
18291             
18292             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18293             v.attr('colspan', function(i, val){
18294                 return parseInt(val) + 1;
18295             });
18296         });
18297                         
18298         
18299         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18300         
18301         this.setStartDate(this.startDate);
18302         this.setEndDate(this.endDate);
18303         
18304         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18305         
18306         this.fillDow();
18307         this.fillMonths();
18308         this.update();
18309         this.showMode();
18310         
18311         if(this.isInline) {
18312             this.show();
18313         }
18314     },
18315     
18316     picker : function()
18317     {
18318         return this.pickerEl;
18319 //        return this.el.select('.datepicker', true).first();
18320     },
18321     
18322     fillDow: function()
18323     {
18324         var dowCnt = this.weekStart;
18325         
18326         var dow = {
18327             tag: 'tr',
18328             cn: [
18329                 
18330             ]
18331         };
18332         
18333         if(this.calendarWeeks){
18334             dow.cn.push({
18335                 tag: 'th',
18336                 cls: 'cw',
18337                 html: '&nbsp;'
18338             })
18339         }
18340         
18341         while (dowCnt < this.weekStart + 7) {
18342             dow.cn.push({
18343                 tag: 'th',
18344                 cls: 'dow',
18345                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18346             });
18347         }
18348         
18349         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18350     },
18351     
18352     fillMonths: function()
18353     {    
18354         var i = 0;
18355         var months = this.picker().select('>.datepicker-months td', true).first();
18356         
18357         months.dom.innerHTML = '';
18358         
18359         while (i < 12) {
18360             var month = {
18361                 tag: 'span',
18362                 cls: 'month',
18363                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18364             };
18365             
18366             months.createChild(month);
18367         }
18368         
18369     },
18370     
18371     update: function()
18372     {
18373         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;
18374         
18375         if (this.date < this.startDate) {
18376             this.viewDate = new Date(this.startDate);
18377         } else if (this.date > this.endDate) {
18378             this.viewDate = new Date(this.endDate);
18379         } else {
18380             this.viewDate = new Date(this.date);
18381         }
18382         
18383         this.fill();
18384     },
18385     
18386     fill: function() 
18387     {
18388         var d = new Date(this.viewDate),
18389                 year = d.getUTCFullYear(),
18390                 month = d.getUTCMonth(),
18391                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18392                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18393                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18394                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18395                 currentDate = this.date && this.date.valueOf(),
18396                 today = this.UTCToday();
18397         
18398         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18399         
18400 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18401         
18402 //        this.picker.select('>tfoot th.today').
18403 //                                              .text(dates[this.language].today)
18404 //                                              .toggle(this.todayBtn !== false);
18405     
18406         this.updateNavArrows();
18407         this.fillMonths();
18408                                                 
18409         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18410         
18411         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18412          
18413         prevMonth.setUTCDate(day);
18414         
18415         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18416         
18417         var nextMonth = new Date(prevMonth);
18418         
18419         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18420         
18421         nextMonth = nextMonth.valueOf();
18422         
18423         var fillMonths = false;
18424         
18425         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18426         
18427         while(prevMonth.valueOf() < nextMonth) {
18428             var clsName = '';
18429             
18430             if (prevMonth.getUTCDay() === this.weekStart) {
18431                 if(fillMonths){
18432                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18433                 }
18434                     
18435                 fillMonths = {
18436                     tag: 'tr',
18437                     cn: []
18438                 };
18439                 
18440                 if(this.calendarWeeks){
18441                     // ISO 8601: First week contains first thursday.
18442                     // ISO also states week starts on Monday, but we can be more abstract here.
18443                     var
18444                     // Start of current week: based on weekstart/current date
18445                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18446                     // Thursday of this week
18447                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18448                     // First Thursday of year, year from thursday
18449                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18450                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18451                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18452                     
18453                     fillMonths.cn.push({
18454                         tag: 'td',
18455                         cls: 'cw',
18456                         html: calWeek
18457                     });
18458                 }
18459             }
18460             
18461             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18462                 clsName += ' old';
18463             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18464                 clsName += ' new';
18465             }
18466             if (this.todayHighlight &&
18467                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18468                 prevMonth.getUTCMonth() == today.getMonth() &&
18469                 prevMonth.getUTCDate() == today.getDate()) {
18470                 clsName += ' today';
18471             }
18472             
18473             if (currentDate && prevMonth.valueOf() === currentDate) {
18474                 clsName += ' active';
18475             }
18476             
18477             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18478                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18479                     clsName += ' disabled';
18480             }
18481             
18482             fillMonths.cn.push({
18483                 tag: 'td',
18484                 cls: 'day ' + clsName,
18485                 html: prevMonth.getDate()
18486             });
18487             
18488             prevMonth.setDate(prevMonth.getDate()+1);
18489         }
18490           
18491         var currentYear = this.date && this.date.getUTCFullYear();
18492         var currentMonth = this.date && this.date.getUTCMonth();
18493         
18494         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18495         
18496         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18497             v.removeClass('active');
18498             
18499             if(currentYear === year && k === currentMonth){
18500                 v.addClass('active');
18501             }
18502             
18503             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18504                 v.addClass('disabled');
18505             }
18506             
18507         });
18508         
18509         
18510         year = parseInt(year/10, 10) * 10;
18511         
18512         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18513         
18514         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18515         
18516         year -= 1;
18517         for (var i = -1; i < 11; i++) {
18518             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18519                 tag: 'span',
18520                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18521                 html: year
18522             });
18523             
18524             year += 1;
18525         }
18526     },
18527     
18528     showMode: function(dir) 
18529     {
18530         if (dir) {
18531             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18532         }
18533         
18534         Roo.each(this.picker().select('>div',true).elements, function(v){
18535             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18536             v.hide();
18537         });
18538         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18539     },
18540     
18541     place: function()
18542     {
18543         if(this.isInline) {
18544             return;
18545         }
18546         
18547         this.picker().removeClass(['bottom', 'top']);
18548         
18549         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18550             /*
18551              * place to the top of element!
18552              *
18553              */
18554             
18555             this.picker().addClass('top');
18556             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18557             
18558             return;
18559         }
18560         
18561         this.picker().addClass('bottom');
18562         
18563         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18564     },
18565     
18566     parseDate : function(value)
18567     {
18568         if(!value || value instanceof Date){
18569             return value;
18570         }
18571         var v = Date.parseDate(value, this.format);
18572         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18573             v = Date.parseDate(value, 'Y-m-d');
18574         }
18575         if(!v && this.altFormats){
18576             if(!this.altFormatsArray){
18577                 this.altFormatsArray = this.altFormats.split("|");
18578             }
18579             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18580                 v = Date.parseDate(value, this.altFormatsArray[i]);
18581             }
18582         }
18583         return v;
18584     },
18585     
18586     formatDate : function(date, fmt)
18587     {   
18588         return (!date || !(date instanceof Date)) ?
18589         date : date.dateFormat(fmt || this.format);
18590     },
18591     
18592     onFocus : function()
18593     {
18594         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18595         this.show();
18596     },
18597     
18598     onBlur : function()
18599     {
18600         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18601         
18602         var d = this.inputEl().getValue();
18603         
18604         this.setValue(d);
18605                 
18606         this.hide();
18607     },
18608     
18609     show : function()
18610     {
18611         this.picker().show();
18612         this.update();
18613         this.place();
18614         
18615         this.fireEvent('show', this, this.date);
18616     },
18617     
18618     hide : function()
18619     {
18620         if(this.isInline) {
18621             return;
18622         }
18623         this.picker().hide();
18624         this.viewMode = this.startViewMode;
18625         this.showMode();
18626         
18627         this.fireEvent('hide', this, this.date);
18628         
18629     },
18630     
18631     onMousedown: function(e)
18632     {
18633         e.stopPropagation();
18634         e.preventDefault();
18635     },
18636     
18637     keyup: function(e)
18638     {
18639         Roo.bootstrap.DateField.superclass.keyup.call(this);
18640         this.update();
18641     },
18642
18643     setValue: function(v)
18644     {
18645         if(this.fireEvent('beforeselect', this, v) !== false){
18646             var d = new Date(this.parseDate(v) ).clearTime();
18647         
18648             if(isNaN(d.getTime())){
18649                 this.date = this.viewDate = '';
18650                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18651                 return;
18652             }
18653
18654             v = this.formatDate(d);
18655
18656             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18657
18658             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18659
18660             this.update();
18661
18662             this.fireEvent('select', this, this.date);
18663         }
18664     },
18665     
18666     getValue: function()
18667     {
18668         return this.formatDate(this.date);
18669     },
18670     
18671     fireKey: function(e)
18672     {
18673         if (!this.picker().isVisible()){
18674             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18675                 this.show();
18676             }
18677             return;
18678         }
18679         
18680         var dateChanged = false,
18681         dir, day, month,
18682         newDate, newViewDate;
18683         
18684         switch(e.keyCode){
18685             case 27: // escape
18686                 this.hide();
18687                 e.preventDefault();
18688                 break;
18689             case 37: // left
18690             case 39: // right
18691                 if (!this.keyboardNavigation) {
18692                     break;
18693                 }
18694                 dir = e.keyCode == 37 ? -1 : 1;
18695                 
18696                 if (e.ctrlKey){
18697                     newDate = this.moveYear(this.date, dir);
18698                     newViewDate = this.moveYear(this.viewDate, dir);
18699                 } else if (e.shiftKey){
18700                     newDate = this.moveMonth(this.date, dir);
18701                     newViewDate = this.moveMonth(this.viewDate, dir);
18702                 } else {
18703                     newDate = new Date(this.date);
18704                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18705                     newViewDate = new Date(this.viewDate);
18706                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18707                 }
18708                 if (this.dateWithinRange(newDate)){
18709                     this.date = newDate;
18710                     this.viewDate = newViewDate;
18711                     this.setValue(this.formatDate(this.date));
18712 //                    this.update();
18713                     e.preventDefault();
18714                     dateChanged = true;
18715                 }
18716                 break;
18717             case 38: // up
18718             case 40: // down
18719                 if (!this.keyboardNavigation) {
18720                     break;
18721                 }
18722                 dir = e.keyCode == 38 ? -1 : 1;
18723                 if (e.ctrlKey){
18724                     newDate = this.moveYear(this.date, dir);
18725                     newViewDate = this.moveYear(this.viewDate, dir);
18726                 } else if (e.shiftKey){
18727                     newDate = this.moveMonth(this.date, dir);
18728                     newViewDate = this.moveMonth(this.viewDate, dir);
18729                 } else {
18730                     newDate = new Date(this.date);
18731                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18732                     newViewDate = new Date(this.viewDate);
18733                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18734                 }
18735                 if (this.dateWithinRange(newDate)){
18736                     this.date = newDate;
18737                     this.viewDate = newViewDate;
18738                     this.setValue(this.formatDate(this.date));
18739 //                    this.update();
18740                     e.preventDefault();
18741                     dateChanged = true;
18742                 }
18743                 break;
18744             case 13: // enter
18745                 this.setValue(this.formatDate(this.date));
18746                 this.hide();
18747                 e.preventDefault();
18748                 break;
18749             case 9: // tab
18750                 this.setValue(this.formatDate(this.date));
18751                 this.hide();
18752                 break;
18753             case 16: // shift
18754             case 17: // ctrl
18755             case 18: // alt
18756                 break;
18757             default :
18758                 this.hide();
18759                 
18760         }
18761     },
18762     
18763     
18764     onClick: function(e) 
18765     {
18766         e.stopPropagation();
18767         e.preventDefault();
18768         
18769         var target = e.getTarget();
18770         
18771         if(target.nodeName.toLowerCase() === 'i'){
18772             target = Roo.get(target).dom.parentNode;
18773         }
18774         
18775         var nodeName = target.nodeName;
18776         var className = target.className;
18777         var html = target.innerHTML;
18778         //Roo.log(nodeName);
18779         
18780         switch(nodeName.toLowerCase()) {
18781             case 'th':
18782                 switch(className) {
18783                     case 'switch':
18784                         this.showMode(1);
18785                         break;
18786                     case 'prev':
18787                     case 'next':
18788                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18789                         switch(this.viewMode){
18790                                 case 0:
18791                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18792                                         break;
18793                                 case 1:
18794                                 case 2:
18795                                         this.viewDate = this.moveYear(this.viewDate, dir);
18796                                         break;
18797                         }
18798                         this.fill();
18799                         break;
18800                     case 'today':
18801                         var date = new Date();
18802                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18803 //                        this.fill()
18804                         this.setValue(this.formatDate(this.date));
18805                         
18806                         this.hide();
18807                         break;
18808                 }
18809                 break;
18810             case 'span':
18811                 if (className.indexOf('disabled') < 0) {
18812                     this.viewDate.setUTCDate(1);
18813                     if (className.indexOf('month') > -1) {
18814                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18815                     } else {
18816                         var year = parseInt(html, 10) || 0;
18817                         this.viewDate.setUTCFullYear(year);
18818                         
18819                     }
18820                     
18821                     if(this.singleMode){
18822                         this.setValue(this.formatDate(this.viewDate));
18823                         this.hide();
18824                         return;
18825                     }
18826                     
18827                     this.showMode(-1);
18828                     this.fill();
18829                 }
18830                 break;
18831                 
18832             case 'td':
18833                 //Roo.log(className);
18834                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18835                     var day = parseInt(html, 10) || 1;
18836                     var year = this.viewDate.getUTCFullYear(),
18837                         month = this.viewDate.getUTCMonth();
18838
18839                     if (className.indexOf('old') > -1) {
18840                         if(month === 0 ){
18841                             month = 11;
18842                             year -= 1;
18843                         }else{
18844                             month -= 1;
18845                         }
18846                     } else if (className.indexOf('new') > -1) {
18847                         if (month == 11) {
18848                             month = 0;
18849                             year += 1;
18850                         } else {
18851                             month += 1;
18852                         }
18853                     }
18854                     //Roo.log([year,month,day]);
18855                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18856                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18857 //                    this.fill();
18858                     //Roo.log(this.formatDate(this.date));
18859                     this.setValue(this.formatDate(this.date));
18860                     this.hide();
18861                 }
18862                 break;
18863         }
18864     },
18865     
18866     setStartDate: function(startDate)
18867     {
18868         this.startDate = startDate || -Infinity;
18869         if (this.startDate !== -Infinity) {
18870             this.startDate = this.parseDate(this.startDate);
18871         }
18872         this.update();
18873         this.updateNavArrows();
18874     },
18875
18876     setEndDate: function(endDate)
18877     {
18878         this.endDate = endDate || Infinity;
18879         if (this.endDate !== Infinity) {
18880             this.endDate = this.parseDate(this.endDate);
18881         }
18882         this.update();
18883         this.updateNavArrows();
18884     },
18885     
18886     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18887     {
18888         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18889         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18890             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18891         }
18892         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18893             return parseInt(d, 10);
18894         });
18895         this.update();
18896         this.updateNavArrows();
18897     },
18898     
18899     updateNavArrows: function() 
18900     {
18901         if(this.singleMode){
18902             return;
18903         }
18904         
18905         var d = new Date(this.viewDate),
18906         year = d.getUTCFullYear(),
18907         month = d.getUTCMonth();
18908         
18909         Roo.each(this.picker().select('.prev', true).elements, function(v){
18910             v.show();
18911             switch (this.viewMode) {
18912                 case 0:
18913
18914                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18915                         v.hide();
18916                     }
18917                     break;
18918                 case 1:
18919                 case 2:
18920                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18921                         v.hide();
18922                     }
18923                     break;
18924             }
18925         });
18926         
18927         Roo.each(this.picker().select('.next', true).elements, function(v){
18928             v.show();
18929             switch (this.viewMode) {
18930                 case 0:
18931
18932                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18933                         v.hide();
18934                     }
18935                     break;
18936                 case 1:
18937                 case 2:
18938                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18939                         v.hide();
18940                     }
18941                     break;
18942             }
18943         })
18944     },
18945     
18946     moveMonth: function(date, dir)
18947     {
18948         if (!dir) {
18949             return date;
18950         }
18951         var new_date = new Date(date.valueOf()),
18952         day = new_date.getUTCDate(),
18953         month = new_date.getUTCMonth(),
18954         mag = Math.abs(dir),
18955         new_month, test;
18956         dir = dir > 0 ? 1 : -1;
18957         if (mag == 1){
18958             test = dir == -1
18959             // If going back one month, make sure month is not current month
18960             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18961             ? function(){
18962                 return new_date.getUTCMonth() == month;
18963             }
18964             // If going forward one month, make sure month is as expected
18965             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18966             : function(){
18967                 return new_date.getUTCMonth() != new_month;
18968             };
18969             new_month = month + dir;
18970             new_date.setUTCMonth(new_month);
18971             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18972             if (new_month < 0 || new_month > 11) {
18973                 new_month = (new_month + 12) % 12;
18974             }
18975         } else {
18976             // For magnitudes >1, move one month at a time...
18977             for (var i=0; i<mag; i++) {
18978                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18979                 new_date = this.moveMonth(new_date, dir);
18980             }
18981             // ...then reset the day, keeping it in the new month
18982             new_month = new_date.getUTCMonth();
18983             new_date.setUTCDate(day);
18984             test = function(){
18985                 return new_month != new_date.getUTCMonth();
18986             };
18987         }
18988         // Common date-resetting loop -- if date is beyond end of month, make it
18989         // end of month
18990         while (test()){
18991             new_date.setUTCDate(--day);
18992             new_date.setUTCMonth(new_month);
18993         }
18994         return new_date;
18995     },
18996
18997     moveYear: function(date, dir)
18998     {
18999         return this.moveMonth(date, dir*12);
19000     },
19001
19002     dateWithinRange: function(date)
19003     {
19004         return date >= this.startDate && date <= this.endDate;
19005     },
19006
19007     
19008     remove: function() 
19009     {
19010         this.picker().remove();
19011     },
19012     
19013     validateValue : function(value)
19014     {
19015         if(value.length < 1)  {
19016             if(this.allowBlank){
19017                 return true;
19018             }
19019             return false;
19020         }
19021         
19022         if(value.length < this.minLength){
19023             return false;
19024         }
19025         if(value.length > this.maxLength){
19026             return false;
19027         }
19028         if(this.vtype){
19029             var vt = Roo.form.VTypes;
19030             if(!vt[this.vtype](value, this)){
19031                 return false;
19032             }
19033         }
19034         if(typeof this.validator == "function"){
19035             var msg = this.validator(value);
19036             if(msg !== true){
19037                 return false;
19038             }
19039         }
19040         
19041         if(this.regex && !this.regex.test(value)){
19042             return false;
19043         }
19044         
19045         if(typeof(this.parseDate(value)) == 'undefined'){
19046             return false;
19047         }
19048         
19049         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19050             return false;
19051         }      
19052         
19053         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19054             return false;
19055         } 
19056         
19057         
19058         return true;
19059     }
19060    
19061 });
19062
19063 Roo.apply(Roo.bootstrap.DateField,  {
19064     
19065     head : {
19066         tag: 'thead',
19067         cn: [
19068         {
19069             tag: 'tr',
19070             cn: [
19071             {
19072                 tag: 'th',
19073                 cls: 'prev',
19074                 html: '<i class="fa fa-arrow-left"/>'
19075             },
19076             {
19077                 tag: 'th',
19078                 cls: 'switch',
19079                 colspan: '5'
19080             },
19081             {
19082                 tag: 'th',
19083                 cls: 'next',
19084                 html: '<i class="fa fa-arrow-right"/>'
19085             }
19086
19087             ]
19088         }
19089         ]
19090     },
19091     
19092     content : {
19093         tag: 'tbody',
19094         cn: [
19095         {
19096             tag: 'tr',
19097             cn: [
19098             {
19099                 tag: 'td',
19100                 colspan: '7'
19101             }
19102             ]
19103         }
19104         ]
19105     },
19106     
19107     footer : {
19108         tag: 'tfoot',
19109         cn: [
19110         {
19111             tag: 'tr',
19112             cn: [
19113             {
19114                 tag: 'th',
19115                 colspan: '7',
19116                 cls: 'today'
19117             }
19118                     
19119             ]
19120         }
19121         ]
19122     },
19123     
19124     dates:{
19125         en: {
19126             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19127             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19128             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19129             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19130             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19131             today: "Today"
19132         }
19133     },
19134     
19135     modes: [
19136     {
19137         clsName: 'days',
19138         navFnc: 'Month',
19139         navStep: 1
19140     },
19141     {
19142         clsName: 'months',
19143         navFnc: 'FullYear',
19144         navStep: 1
19145     },
19146     {
19147         clsName: 'years',
19148         navFnc: 'FullYear',
19149         navStep: 10
19150     }]
19151 });
19152
19153 Roo.apply(Roo.bootstrap.DateField,  {
19154   
19155     template : {
19156         tag: 'div',
19157         cls: 'datepicker dropdown-menu roo-dynamic',
19158         cn: [
19159         {
19160             tag: 'div',
19161             cls: 'datepicker-days',
19162             cn: [
19163             {
19164                 tag: 'table',
19165                 cls: 'table-condensed',
19166                 cn:[
19167                 Roo.bootstrap.DateField.head,
19168                 {
19169                     tag: 'tbody'
19170                 },
19171                 Roo.bootstrap.DateField.footer
19172                 ]
19173             }
19174             ]
19175         },
19176         {
19177             tag: 'div',
19178             cls: 'datepicker-months',
19179             cn: [
19180             {
19181                 tag: 'table',
19182                 cls: 'table-condensed',
19183                 cn:[
19184                 Roo.bootstrap.DateField.head,
19185                 Roo.bootstrap.DateField.content,
19186                 Roo.bootstrap.DateField.footer
19187                 ]
19188             }
19189             ]
19190         },
19191         {
19192             tag: 'div',
19193             cls: 'datepicker-years',
19194             cn: [
19195             {
19196                 tag: 'table',
19197                 cls: 'table-condensed',
19198                 cn:[
19199                 Roo.bootstrap.DateField.head,
19200                 Roo.bootstrap.DateField.content,
19201                 Roo.bootstrap.DateField.footer
19202                 ]
19203             }
19204             ]
19205         }
19206         ]
19207     }
19208 });
19209
19210  
19211
19212  /*
19213  * - LGPL
19214  *
19215  * TimeField
19216  * 
19217  */
19218
19219 /**
19220  * @class Roo.bootstrap.TimeField
19221  * @extends Roo.bootstrap.Input
19222  * Bootstrap DateField class
19223  * 
19224  * 
19225  * @constructor
19226  * Create a new TimeField
19227  * @param {Object} config The config object
19228  */
19229
19230 Roo.bootstrap.TimeField = function(config){
19231     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19232     this.addEvents({
19233             /**
19234              * @event show
19235              * Fires when this field show.
19236              * @param {Roo.bootstrap.DateField} thisthis
19237              * @param {Mixed} date The date value
19238              */
19239             show : true,
19240             /**
19241              * @event show
19242              * Fires when this field hide.
19243              * @param {Roo.bootstrap.DateField} this
19244              * @param {Mixed} date The date value
19245              */
19246             hide : true,
19247             /**
19248              * @event select
19249              * Fires when select a date.
19250              * @param {Roo.bootstrap.DateField} this
19251              * @param {Mixed} date The date value
19252              */
19253             select : true
19254         });
19255 };
19256
19257 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19258     
19259     /**
19260      * @cfg {String} format
19261      * The default time format string which can be overriden for localization support.  The format must be
19262      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19263      */
19264     format : "H:i",
19265        
19266     onRender: function(ct, position)
19267     {
19268         
19269         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19270                 
19271         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19272         
19273         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19274         
19275         this.pop = this.picker().select('>.datepicker-time',true).first();
19276         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19277         
19278         this.picker().on('mousedown', this.onMousedown, this);
19279         this.picker().on('click', this.onClick, this);
19280         
19281         this.picker().addClass('datepicker-dropdown');
19282     
19283         this.fillTime();
19284         this.update();
19285             
19286         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19287         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19288         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19289         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19290         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19291         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19292
19293     },
19294     
19295     fireKey: function(e){
19296         if (!this.picker().isVisible()){
19297             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19298                 this.show();
19299             }
19300             return;
19301         }
19302
19303         e.preventDefault();
19304         
19305         switch(e.keyCode){
19306             case 27: // escape
19307                 this.hide();
19308                 break;
19309             case 37: // left
19310             case 39: // right
19311                 this.onTogglePeriod();
19312                 break;
19313             case 38: // up
19314                 this.onIncrementMinutes();
19315                 break;
19316             case 40: // down
19317                 this.onDecrementMinutes();
19318                 break;
19319             case 13: // enter
19320             case 9: // tab
19321                 this.setTime();
19322                 break;
19323         }
19324     },
19325     
19326     onClick: function(e) {
19327         e.stopPropagation();
19328         e.preventDefault();
19329     },
19330     
19331     picker : function()
19332     {
19333         return this.el.select('.datepicker', true).first();
19334     },
19335     
19336     fillTime: function()
19337     {    
19338         var time = this.pop.select('tbody', true).first();
19339         
19340         time.dom.innerHTML = '';
19341         
19342         time.createChild({
19343             tag: 'tr',
19344             cn: [
19345                 {
19346                     tag: 'td',
19347                     cn: [
19348                         {
19349                             tag: 'a',
19350                             href: '#',
19351                             cls: 'btn',
19352                             cn: [
19353                                 {
19354                                     tag: 'span',
19355                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19356                                 }
19357                             ]
19358                         } 
19359                     ]
19360                 },
19361                 {
19362                     tag: 'td',
19363                     cls: 'separator'
19364                 },
19365                 {
19366                     tag: 'td',
19367                     cn: [
19368                         {
19369                             tag: 'a',
19370                             href: '#',
19371                             cls: 'btn',
19372                             cn: [
19373                                 {
19374                                     tag: 'span',
19375                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19376                                 }
19377                             ]
19378                         }
19379                     ]
19380                 },
19381                 {
19382                     tag: 'td',
19383                     cls: 'separator'
19384                 }
19385             ]
19386         });
19387         
19388         time.createChild({
19389             tag: 'tr',
19390             cn: [
19391                 {
19392                     tag: 'td',
19393                     cn: [
19394                         {
19395                             tag: 'span',
19396                             cls: 'timepicker-hour',
19397                             html: '00'
19398                         }  
19399                     ]
19400                 },
19401                 {
19402                     tag: 'td',
19403                     cls: 'separator',
19404                     html: ':'
19405                 },
19406                 {
19407                     tag: 'td',
19408                     cn: [
19409                         {
19410                             tag: 'span',
19411                             cls: 'timepicker-minute',
19412                             html: '00'
19413                         }  
19414                     ]
19415                 },
19416                 {
19417                     tag: 'td',
19418                     cls: 'separator'
19419                 },
19420                 {
19421                     tag: 'td',
19422                     cn: [
19423                         {
19424                             tag: 'button',
19425                             type: 'button',
19426                             cls: 'btn btn-primary period',
19427                             html: 'AM'
19428                             
19429                         }
19430                     ]
19431                 }
19432             ]
19433         });
19434         
19435         time.createChild({
19436             tag: 'tr',
19437             cn: [
19438                 {
19439                     tag: 'td',
19440                     cn: [
19441                         {
19442                             tag: 'a',
19443                             href: '#',
19444                             cls: 'btn',
19445                             cn: [
19446                                 {
19447                                     tag: 'span',
19448                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19449                                 }
19450                             ]
19451                         }
19452                     ]
19453                 },
19454                 {
19455                     tag: 'td',
19456                     cls: 'separator'
19457                 },
19458                 {
19459                     tag: 'td',
19460                     cn: [
19461                         {
19462                             tag: 'a',
19463                             href: '#',
19464                             cls: 'btn',
19465                             cn: [
19466                                 {
19467                                     tag: 'span',
19468                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19469                                 }
19470                             ]
19471                         }
19472                     ]
19473                 },
19474                 {
19475                     tag: 'td',
19476                     cls: 'separator'
19477                 }
19478             ]
19479         });
19480         
19481     },
19482     
19483     update: function()
19484     {
19485         
19486         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19487         
19488         this.fill();
19489     },
19490     
19491     fill: function() 
19492     {
19493         var hours = this.time.getHours();
19494         var minutes = this.time.getMinutes();
19495         var period = 'AM';
19496         
19497         if(hours > 11){
19498             period = 'PM';
19499         }
19500         
19501         if(hours == 0){
19502             hours = 12;
19503         }
19504         
19505         
19506         if(hours > 12){
19507             hours = hours - 12;
19508         }
19509         
19510         if(hours < 10){
19511             hours = '0' + hours;
19512         }
19513         
19514         if(minutes < 10){
19515             minutes = '0' + minutes;
19516         }
19517         
19518         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19519         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19520         this.pop.select('button', true).first().dom.innerHTML = period;
19521         
19522     },
19523     
19524     place: function()
19525     {   
19526         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19527         
19528         var cls = ['bottom'];
19529         
19530         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19531             cls.pop();
19532             cls.push('top');
19533         }
19534         
19535         cls.push('right');
19536         
19537         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19538             cls.pop();
19539             cls.push('left');
19540         }
19541         
19542         this.picker().addClass(cls.join('-'));
19543         
19544         var _this = this;
19545         
19546         Roo.each(cls, function(c){
19547             if(c == 'bottom'){
19548                 _this.picker().setTop(_this.inputEl().getHeight());
19549                 return;
19550             }
19551             if(c == 'top'){
19552                 _this.picker().setTop(0 - _this.picker().getHeight());
19553                 return;
19554             }
19555             
19556             if(c == 'left'){
19557                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19558                 return;
19559             }
19560             if(c == 'right'){
19561                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19562                 return;
19563             }
19564         });
19565         
19566     },
19567   
19568     onFocus : function()
19569     {
19570         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19571         this.show();
19572     },
19573     
19574     onBlur : function()
19575     {
19576         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19577         this.hide();
19578     },
19579     
19580     show : function()
19581     {
19582         this.picker().show();
19583         this.pop.show();
19584         this.update();
19585         this.place();
19586         
19587         this.fireEvent('show', this, this.date);
19588     },
19589     
19590     hide : function()
19591     {
19592         this.picker().hide();
19593         this.pop.hide();
19594         
19595         this.fireEvent('hide', this, this.date);
19596     },
19597     
19598     setTime : function()
19599     {
19600         this.hide();
19601         this.setValue(this.time.format(this.format));
19602         
19603         this.fireEvent('select', this, this.date);
19604         
19605         
19606     },
19607     
19608     onMousedown: function(e){
19609         e.stopPropagation();
19610         e.preventDefault();
19611     },
19612     
19613     onIncrementHours: function()
19614     {
19615         Roo.log('onIncrementHours');
19616         this.time = this.time.add(Date.HOUR, 1);
19617         this.update();
19618         
19619     },
19620     
19621     onDecrementHours: function()
19622     {
19623         Roo.log('onDecrementHours');
19624         this.time = this.time.add(Date.HOUR, -1);
19625         this.update();
19626     },
19627     
19628     onIncrementMinutes: function()
19629     {
19630         Roo.log('onIncrementMinutes');
19631         this.time = this.time.add(Date.MINUTE, 1);
19632         this.update();
19633     },
19634     
19635     onDecrementMinutes: function()
19636     {
19637         Roo.log('onDecrementMinutes');
19638         this.time = this.time.add(Date.MINUTE, -1);
19639         this.update();
19640     },
19641     
19642     onTogglePeriod: function()
19643     {
19644         Roo.log('onTogglePeriod');
19645         this.time = this.time.add(Date.HOUR, 12);
19646         this.update();
19647     }
19648     
19649    
19650 });
19651
19652 Roo.apply(Roo.bootstrap.TimeField,  {
19653     
19654     content : {
19655         tag: 'tbody',
19656         cn: [
19657             {
19658                 tag: 'tr',
19659                 cn: [
19660                 {
19661                     tag: 'td',
19662                     colspan: '7'
19663                 }
19664                 ]
19665             }
19666         ]
19667     },
19668     
19669     footer : {
19670         tag: 'tfoot',
19671         cn: [
19672             {
19673                 tag: 'tr',
19674                 cn: [
19675                 {
19676                     tag: 'th',
19677                     colspan: '7',
19678                     cls: '',
19679                     cn: [
19680                         {
19681                             tag: 'button',
19682                             cls: 'btn btn-info ok',
19683                             html: 'OK'
19684                         }
19685                     ]
19686                 }
19687
19688                 ]
19689             }
19690         ]
19691     }
19692 });
19693
19694 Roo.apply(Roo.bootstrap.TimeField,  {
19695   
19696     template : {
19697         tag: 'div',
19698         cls: 'datepicker dropdown-menu',
19699         cn: [
19700             {
19701                 tag: 'div',
19702                 cls: 'datepicker-time',
19703                 cn: [
19704                 {
19705                     tag: 'table',
19706                     cls: 'table-condensed',
19707                     cn:[
19708                     Roo.bootstrap.TimeField.content,
19709                     Roo.bootstrap.TimeField.footer
19710                     ]
19711                 }
19712                 ]
19713             }
19714         ]
19715     }
19716 });
19717
19718  
19719
19720  /*
19721  * - LGPL
19722  *
19723  * MonthField
19724  * 
19725  */
19726
19727 /**
19728  * @class Roo.bootstrap.MonthField
19729  * @extends Roo.bootstrap.Input
19730  * Bootstrap MonthField class
19731  * 
19732  * @cfg {String} language default en
19733  * 
19734  * @constructor
19735  * Create a new MonthField
19736  * @param {Object} config The config object
19737  */
19738
19739 Roo.bootstrap.MonthField = function(config){
19740     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19741     
19742     this.addEvents({
19743         /**
19744          * @event show
19745          * Fires when this field show.
19746          * @param {Roo.bootstrap.MonthField} this
19747          * @param {Mixed} date The date value
19748          */
19749         show : true,
19750         /**
19751          * @event show
19752          * Fires when this field hide.
19753          * @param {Roo.bootstrap.MonthField} this
19754          * @param {Mixed} date The date value
19755          */
19756         hide : true,
19757         /**
19758          * @event select
19759          * Fires when select a date.
19760          * @param {Roo.bootstrap.MonthField} this
19761          * @param {String} oldvalue The old value
19762          * @param {String} newvalue The new value
19763          */
19764         select : true
19765     });
19766 };
19767
19768 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19769     
19770     onRender: function(ct, position)
19771     {
19772         
19773         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19774         
19775         this.language = this.language || 'en';
19776         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19777         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19778         
19779         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19780         this.isInline = false;
19781         this.isInput = true;
19782         this.component = this.el.select('.add-on', true).first() || false;
19783         this.component = (this.component && this.component.length === 0) ? false : this.component;
19784         this.hasInput = this.component && this.inputEL().length;
19785         
19786         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19787         
19788         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19789         
19790         this.picker().on('mousedown', this.onMousedown, this);
19791         this.picker().on('click', this.onClick, this);
19792         
19793         this.picker().addClass('datepicker-dropdown');
19794         
19795         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19796             v.setStyle('width', '189px');
19797         });
19798         
19799         this.fillMonths();
19800         
19801         this.update();
19802         
19803         if(this.isInline) {
19804             this.show();
19805         }
19806         
19807     },
19808     
19809     setValue: function(v, suppressEvent)
19810     {   
19811         var o = this.getValue();
19812         
19813         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19814         
19815         this.update();
19816
19817         if(suppressEvent !== true){
19818             this.fireEvent('select', this, o, v);
19819         }
19820         
19821     },
19822     
19823     getValue: function()
19824     {
19825         return this.value;
19826     },
19827     
19828     onClick: function(e) 
19829     {
19830         e.stopPropagation();
19831         e.preventDefault();
19832         
19833         var target = e.getTarget();
19834         
19835         if(target.nodeName.toLowerCase() === 'i'){
19836             target = Roo.get(target).dom.parentNode;
19837         }
19838         
19839         var nodeName = target.nodeName;
19840         var className = target.className;
19841         var html = target.innerHTML;
19842         
19843         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19844             return;
19845         }
19846         
19847         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19848         
19849         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19850         
19851         this.hide();
19852                         
19853     },
19854     
19855     picker : function()
19856     {
19857         return this.pickerEl;
19858     },
19859     
19860     fillMonths: function()
19861     {    
19862         var i = 0;
19863         var months = this.picker().select('>.datepicker-months td', true).first();
19864         
19865         months.dom.innerHTML = '';
19866         
19867         while (i < 12) {
19868             var month = {
19869                 tag: 'span',
19870                 cls: 'month',
19871                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19872             };
19873             
19874             months.createChild(month);
19875         }
19876         
19877     },
19878     
19879     update: function()
19880     {
19881         var _this = this;
19882         
19883         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19884             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19885         }
19886         
19887         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19888             e.removeClass('active');
19889             
19890             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19891                 e.addClass('active');
19892             }
19893         })
19894     },
19895     
19896     place: function()
19897     {
19898         if(this.isInline) {
19899             return;
19900         }
19901         
19902         this.picker().removeClass(['bottom', 'top']);
19903         
19904         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19905             /*
19906              * place to the top of element!
19907              *
19908              */
19909             
19910             this.picker().addClass('top');
19911             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19912             
19913             return;
19914         }
19915         
19916         this.picker().addClass('bottom');
19917         
19918         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19919     },
19920     
19921     onFocus : function()
19922     {
19923         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19924         this.show();
19925     },
19926     
19927     onBlur : function()
19928     {
19929         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19930         
19931         var d = this.inputEl().getValue();
19932         
19933         this.setValue(d);
19934                 
19935         this.hide();
19936     },
19937     
19938     show : function()
19939     {
19940         this.picker().show();
19941         this.picker().select('>.datepicker-months', true).first().show();
19942         this.update();
19943         this.place();
19944         
19945         this.fireEvent('show', this, this.date);
19946     },
19947     
19948     hide : function()
19949     {
19950         if(this.isInline) {
19951             return;
19952         }
19953         this.picker().hide();
19954         this.fireEvent('hide', this, this.date);
19955         
19956     },
19957     
19958     onMousedown: function(e)
19959     {
19960         e.stopPropagation();
19961         e.preventDefault();
19962     },
19963     
19964     keyup: function(e)
19965     {
19966         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19967         this.update();
19968     },
19969
19970     fireKey: function(e)
19971     {
19972         if (!this.picker().isVisible()){
19973             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19974                 this.show();
19975             }
19976             return;
19977         }
19978         
19979         var dir;
19980         
19981         switch(e.keyCode){
19982             case 27: // escape
19983                 this.hide();
19984                 e.preventDefault();
19985                 break;
19986             case 37: // left
19987             case 39: // right
19988                 dir = e.keyCode == 37 ? -1 : 1;
19989                 
19990                 this.vIndex = this.vIndex + dir;
19991                 
19992                 if(this.vIndex < 0){
19993                     this.vIndex = 0;
19994                 }
19995                 
19996                 if(this.vIndex > 11){
19997                     this.vIndex = 11;
19998                 }
19999                 
20000                 if(isNaN(this.vIndex)){
20001                     this.vIndex = 0;
20002                 }
20003                 
20004                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20005                 
20006                 break;
20007             case 38: // up
20008             case 40: // down
20009                 
20010                 dir = e.keyCode == 38 ? -1 : 1;
20011                 
20012                 this.vIndex = this.vIndex + dir * 4;
20013                 
20014                 if(this.vIndex < 0){
20015                     this.vIndex = 0;
20016                 }
20017                 
20018                 if(this.vIndex > 11){
20019                     this.vIndex = 11;
20020                 }
20021                 
20022                 if(isNaN(this.vIndex)){
20023                     this.vIndex = 0;
20024                 }
20025                 
20026                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20027                 break;
20028                 
20029             case 13: // enter
20030                 
20031                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20032                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20033                 }
20034                 
20035                 this.hide();
20036                 e.preventDefault();
20037                 break;
20038             case 9: // tab
20039                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20040                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20041                 }
20042                 this.hide();
20043                 break;
20044             case 16: // shift
20045             case 17: // ctrl
20046             case 18: // alt
20047                 break;
20048             default :
20049                 this.hide();
20050                 
20051         }
20052     },
20053     
20054     remove: function() 
20055     {
20056         this.picker().remove();
20057     }
20058    
20059 });
20060
20061 Roo.apply(Roo.bootstrap.MonthField,  {
20062     
20063     content : {
20064         tag: 'tbody',
20065         cn: [
20066         {
20067             tag: 'tr',
20068             cn: [
20069             {
20070                 tag: 'td',
20071                 colspan: '7'
20072             }
20073             ]
20074         }
20075         ]
20076     },
20077     
20078     dates:{
20079         en: {
20080             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20081             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20082         }
20083     }
20084 });
20085
20086 Roo.apply(Roo.bootstrap.MonthField,  {
20087   
20088     template : {
20089         tag: 'div',
20090         cls: 'datepicker dropdown-menu roo-dynamic',
20091         cn: [
20092             {
20093                 tag: 'div',
20094                 cls: 'datepicker-months',
20095                 cn: [
20096                 {
20097                     tag: 'table',
20098                     cls: 'table-condensed',
20099                     cn:[
20100                         Roo.bootstrap.DateField.content
20101                     ]
20102                 }
20103                 ]
20104             }
20105         ]
20106     }
20107 });
20108
20109  
20110
20111  
20112  /*
20113  * - LGPL
20114  *
20115  * CheckBox
20116  * 
20117  */
20118
20119 /**
20120  * @class Roo.bootstrap.CheckBox
20121  * @extends Roo.bootstrap.Input
20122  * Bootstrap CheckBox class
20123  * 
20124  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20125  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20126  * @cfg {String} boxLabel The text that appears beside the checkbox
20127  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20128  * @cfg {Boolean} checked initnal the element
20129  * @cfg {Boolean} inline inline the element (default false)
20130  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20131  * 
20132  * @constructor
20133  * Create a new CheckBox
20134  * @param {Object} config The config object
20135  */
20136
20137 Roo.bootstrap.CheckBox = function(config){
20138     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20139    
20140     this.addEvents({
20141         /**
20142         * @event check
20143         * Fires when the element is checked or unchecked.
20144         * @param {Roo.bootstrap.CheckBox} this This input
20145         * @param {Boolean} checked The new checked value
20146         */
20147        check : true
20148     });
20149     
20150 };
20151
20152 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20153   
20154     inputType: 'checkbox',
20155     inputValue: 1,
20156     valueOff: 0,
20157     boxLabel: false,
20158     checked: false,
20159     weight : false,
20160     inline: false,
20161     
20162     getAutoCreate : function()
20163     {
20164         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20165         
20166         var id = Roo.id();
20167         
20168         var cfg = {};
20169         
20170         cfg.cls = 'form-group ' + this.inputType; //input-group
20171         
20172         if(this.inline){
20173             cfg.cls += ' ' + this.inputType + '-inline';
20174         }
20175         
20176         var input =  {
20177             tag: 'input',
20178             id : id,
20179             type : this.inputType,
20180             value : this.inputValue,
20181             cls : 'roo-' + this.inputType, //'form-box',
20182             placeholder : this.placeholder || ''
20183             
20184         };
20185         
20186         if(this.inputType != 'radio'){
20187             var hidden =  {
20188                 tag: 'input',
20189                 type : 'hidden',
20190                 cls : 'roo-hidden-value',
20191                 value : this.checked ? this.valueOff : this.inputValue
20192             };
20193         }
20194         
20195             
20196         if (this.weight) { // Validity check?
20197             cfg.cls += " " + this.inputType + "-" + this.weight;
20198         }
20199         
20200         if (this.disabled) {
20201             input.disabled=true;
20202         }
20203         
20204         if(this.checked){
20205             input.checked = this.checked;
20206             
20207         }
20208         
20209         
20210         if (this.name) {
20211             
20212             input.name = this.name;
20213             
20214             if(this.inputType != 'radio'){
20215                 hidden.name = this.name;
20216                 input.name = '_hidden_' + this.name;
20217             }
20218         }
20219         
20220         if (this.size) {
20221             input.cls += ' input-' + this.size;
20222         }
20223         
20224         var settings=this;
20225         
20226         ['xs','sm','md','lg'].map(function(size){
20227             if (settings[size]) {
20228                 cfg.cls += ' col-' + size + '-' + settings[size];
20229             }
20230         });
20231         
20232         var inputblock = input;
20233          
20234         if (this.before || this.after) {
20235             
20236             inputblock = {
20237                 cls : 'input-group',
20238                 cn :  [] 
20239             };
20240             
20241             if (this.before) {
20242                 inputblock.cn.push({
20243                     tag :'span',
20244                     cls : 'input-group-addon',
20245                     html : this.before
20246                 });
20247             }
20248             
20249             inputblock.cn.push(input);
20250             
20251             if(this.inputType != 'radio'){
20252                 inputblock.cn.push(hidden);
20253             }
20254             
20255             if (this.after) {
20256                 inputblock.cn.push({
20257                     tag :'span',
20258                     cls : 'input-group-addon',
20259                     html : this.after
20260                 });
20261             }
20262             
20263         }
20264         
20265         if (align ==='left' && this.fieldLabel.length) {
20266 //                Roo.log("left and has label");
20267             cfg.cn = [
20268                 {
20269                     tag: 'label',
20270                     'for' :  id,
20271                     cls : 'control-label',
20272                     html : this.fieldLabel
20273
20274                 },
20275                 {
20276                     cls : "", 
20277                     cn: [
20278                         inputblock
20279                     ]
20280                 }
20281             ];
20282             
20283             if(this.labelWidth > 12){
20284                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20285             }
20286             
20287             if(this.labelWidth < 13 && this.labelmd == 0){
20288                 this.labelmd = this.labelWidth;
20289             }
20290             
20291             if(this.labellg > 0){
20292                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20293                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20294             }
20295             
20296             if(this.labelmd > 0){
20297                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20298                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20299             }
20300             
20301             if(this.labelsm > 0){
20302                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20303                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20304             }
20305             
20306             if(this.labelxs > 0){
20307                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20308                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20309             }
20310             
20311         } else if ( this.fieldLabel.length) {
20312 //                Roo.log(" label");
20313                 cfg.cn = [
20314                    
20315                     {
20316                         tag: this.boxLabel ? 'span' : 'label',
20317                         'for': id,
20318                         cls: 'control-label box-input-label',
20319                         //cls : 'input-group-addon',
20320                         html : this.fieldLabel
20321                         
20322                     },
20323                     
20324                     inputblock
20325                     
20326                 ];
20327
20328         } else {
20329             
20330 //                Roo.log(" no label && no align");
20331                 cfg.cn = [  inputblock ] ;
20332                 
20333                 
20334         }
20335         
20336         if(this.boxLabel){
20337              var boxLabelCfg = {
20338                 tag: 'label',
20339                 //'for': id, // box label is handled by onclick - so no for...
20340                 cls: 'box-label',
20341                 html: this.boxLabel
20342             };
20343             
20344             if(this.tooltip){
20345                 boxLabelCfg.tooltip = this.tooltip;
20346             }
20347              
20348             cfg.cn.push(boxLabelCfg);
20349         }
20350         
20351         if(this.inputType != 'radio'){
20352             cfg.cn.push(hidden);
20353         }
20354         
20355         return cfg;
20356         
20357     },
20358     
20359     /**
20360      * return the real input element.
20361      */
20362     inputEl: function ()
20363     {
20364         return this.el.select('input.roo-' + this.inputType,true).first();
20365     },
20366     hiddenEl: function ()
20367     {
20368         return this.el.select('input.roo-hidden-value',true).first();
20369     },
20370     
20371     labelEl: function()
20372     {
20373         return this.el.select('label.control-label',true).first();
20374     },
20375     /* depricated... */
20376     
20377     label: function()
20378     {
20379         return this.labelEl();
20380     },
20381     
20382     boxLabelEl: function()
20383     {
20384         return this.el.select('label.box-label',true).first();
20385     },
20386     
20387     initEvents : function()
20388     {
20389 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20390         
20391         this.inputEl().on('click', this.onClick,  this);
20392         
20393         if (this.boxLabel) { 
20394             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20395         }
20396         
20397         this.startValue = this.getValue();
20398         
20399         if(this.groupId){
20400             Roo.bootstrap.CheckBox.register(this);
20401         }
20402     },
20403     
20404     onClick : function()
20405     {   
20406         this.setChecked(!this.checked);
20407     },
20408     
20409     setChecked : function(state,suppressEvent)
20410     {
20411         this.startValue = this.getValue();
20412
20413         if(this.inputType == 'radio'){
20414             
20415             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20416                 e.dom.checked = false;
20417             });
20418             
20419             this.inputEl().dom.checked = true;
20420             
20421             this.inputEl().dom.value = this.inputValue;
20422             
20423             if(suppressEvent !== true){
20424                 this.fireEvent('check', this, true);
20425             }
20426             
20427             this.validate();
20428             
20429             return;
20430         }
20431         
20432         this.checked = state;
20433         
20434         this.inputEl().dom.checked = state;
20435         
20436         
20437         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20438         
20439         if(suppressEvent !== true){
20440             this.fireEvent('check', this, state);
20441         }
20442         
20443         this.validate();
20444     },
20445     
20446     getValue : function()
20447     {
20448         if(this.inputType == 'radio'){
20449             return this.getGroupValue();
20450         }
20451         
20452         return this.hiddenEl().dom.value;
20453         
20454     },
20455     
20456     getGroupValue : function()
20457     {
20458         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20459             return '';
20460         }
20461         
20462         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20463     },
20464     
20465     setValue : function(v,suppressEvent)
20466     {
20467         if(this.inputType == 'radio'){
20468             this.setGroupValue(v, suppressEvent);
20469             return;
20470         }
20471         
20472         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20473         
20474         this.validate();
20475     },
20476     
20477     setGroupValue : function(v, suppressEvent)
20478     {
20479         this.startValue = this.getValue();
20480         
20481         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20482             e.dom.checked = false;
20483             
20484             if(e.dom.value == v){
20485                 e.dom.checked = true;
20486             }
20487         });
20488         
20489         if(suppressEvent !== true){
20490             this.fireEvent('check', this, true);
20491         }
20492
20493         this.validate();
20494         
20495         return;
20496     },
20497     
20498     validate : function()
20499     {
20500         if(
20501                 this.disabled || 
20502                 (this.inputType == 'radio' && this.validateRadio()) ||
20503                 (this.inputType == 'checkbox' && this.validateCheckbox())
20504         ){
20505             this.markValid();
20506             return true;
20507         }
20508         
20509         this.markInvalid();
20510         return false;
20511     },
20512     
20513     validateRadio : function()
20514     {
20515         if(this.allowBlank){
20516             return true;
20517         }
20518         
20519         var valid = false;
20520         
20521         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20522             if(!e.dom.checked){
20523                 return;
20524             }
20525             
20526             valid = true;
20527             
20528             return false;
20529         });
20530         
20531         return valid;
20532     },
20533     
20534     validateCheckbox : function()
20535     {
20536         if(!this.groupId){
20537             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20538             //return (this.getValue() == this.inputValue) ? true : false;
20539         }
20540         
20541         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20542         
20543         if(!group){
20544             return false;
20545         }
20546         
20547         var r = false;
20548         
20549         for(var i in group){
20550             if(r){
20551                 break;
20552             }
20553             
20554             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20555         }
20556         
20557         return r;
20558     },
20559     
20560     /**
20561      * Mark this field as valid
20562      */
20563     markValid : function()
20564     {
20565         var _this = this;
20566         
20567         this.fireEvent('valid', this);
20568         
20569         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20570         
20571         if(this.groupId){
20572             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20573         }
20574         
20575         if(label){
20576             label.markValid();
20577         }
20578
20579         if(this.inputType == 'radio'){
20580             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20581                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20582                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20583             });
20584             
20585             return;
20586         }
20587
20588         if(!this.groupId){
20589             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20590             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20591             return;
20592         }
20593         
20594         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20595         
20596         if(!group){
20597             return;
20598         }
20599         
20600         for(var i in group){
20601             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20602             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20603         }
20604     },
20605     
20606      /**
20607      * Mark this field as invalid
20608      * @param {String} msg The validation message
20609      */
20610     markInvalid : function(msg)
20611     {
20612         if(this.allowBlank){
20613             return;
20614         }
20615         
20616         var _this = this;
20617         
20618         this.fireEvent('invalid', this, msg);
20619         
20620         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20621         
20622         if(this.groupId){
20623             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20624         }
20625         
20626         if(label){
20627             label.markInvalid();
20628         }
20629             
20630         if(this.inputType == 'radio'){
20631             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20632                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20633                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20634             });
20635             
20636             return;
20637         }
20638         
20639         if(!this.groupId){
20640             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20641             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20642             return;
20643         }
20644         
20645         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20646         
20647         if(!group){
20648             return;
20649         }
20650         
20651         for(var i in group){
20652             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20653             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20654         }
20655         
20656     },
20657     
20658     clearInvalid : function()
20659     {
20660         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20661         
20662         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20663         
20664         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20665         
20666         if (label) {
20667             label.iconEl.removeClass(label.validClass);
20668             label.iconEl.removeClass(label.invalidClass);
20669         }
20670     },
20671     
20672     disable : function()
20673     {
20674         if(this.inputType != 'radio'){
20675             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20676             return;
20677         }
20678         
20679         var _this = this;
20680         
20681         if(this.rendered){
20682             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20683                 _this.getActionEl().addClass(this.disabledClass);
20684                 e.dom.disabled = true;
20685             });
20686         }
20687         
20688         this.disabled = true;
20689         this.fireEvent("disable", this);
20690         return this;
20691     },
20692
20693     enable : function()
20694     {
20695         if(this.inputType != 'radio'){
20696             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20697             return;
20698         }
20699         
20700         var _this = this;
20701         
20702         if(this.rendered){
20703             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20704                 _this.getActionEl().removeClass(this.disabledClass);
20705                 e.dom.disabled = false;
20706             });
20707         }
20708         
20709         this.disabled = false;
20710         this.fireEvent("enable", this);
20711         return this;
20712     }
20713
20714 });
20715
20716 Roo.apply(Roo.bootstrap.CheckBox, {
20717     
20718     groups: {},
20719     
20720      /**
20721     * register a CheckBox Group
20722     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20723     */
20724     register : function(checkbox)
20725     {
20726         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20727             this.groups[checkbox.groupId] = {};
20728         }
20729         
20730         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20731             return;
20732         }
20733         
20734         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20735         
20736     },
20737     /**
20738     * fetch a CheckBox Group based on the group ID
20739     * @param {string} the group ID
20740     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20741     */
20742     get: function(groupId) {
20743         if (typeof(this.groups[groupId]) == 'undefined') {
20744             return false;
20745         }
20746         
20747         return this.groups[groupId] ;
20748     }
20749     
20750     
20751 });
20752 /*
20753  * - LGPL
20754  *
20755  * RadioItem
20756  * 
20757  */
20758
20759 /**
20760  * @class Roo.bootstrap.Radio
20761  * @extends Roo.bootstrap.Component
20762  * Bootstrap Radio class
20763  * @cfg {String} boxLabel - the label associated
20764  * @cfg {String} value - the value of radio
20765  * 
20766  * @constructor
20767  * Create a new Radio
20768  * @param {Object} config The config object
20769  */
20770 Roo.bootstrap.Radio = function(config){
20771     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20772     
20773 };
20774
20775 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20776     
20777     boxLabel : '',
20778     
20779     value : '',
20780     
20781     getAutoCreate : function()
20782     {
20783         var cfg = {
20784             tag : 'div',
20785             cls : 'form-group radio',
20786             cn : [
20787                 {
20788                     tag : 'label',
20789                     cls : 'box-label',
20790                     html : this.boxLabel
20791                 }
20792             ]
20793         };
20794         
20795         return cfg;
20796     },
20797     
20798     initEvents : function() 
20799     {
20800         this.parent().register(this);
20801         
20802         this.el.on('click', this.onClick, this);
20803         
20804     },
20805     
20806     onClick : function()
20807     {
20808         this.setChecked(true);
20809     },
20810     
20811     setChecked : function(state, suppressEvent)
20812     {
20813         this.parent().setValue(this.value, suppressEvent);
20814         
20815     },
20816     
20817     setBoxLabel : function(v)
20818     {
20819         this.boxLabel = v;
20820         
20821         if(this.rendered){
20822             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20823         }
20824     }
20825     
20826 });
20827  
20828
20829  /*
20830  * - LGPL
20831  *
20832  * Input
20833  * 
20834  */
20835
20836 /**
20837  * @class Roo.bootstrap.SecurePass
20838  * @extends Roo.bootstrap.Input
20839  * Bootstrap SecurePass class
20840  *
20841  * 
20842  * @constructor
20843  * Create a new SecurePass
20844  * @param {Object} config The config object
20845  */
20846  
20847 Roo.bootstrap.SecurePass = function (config) {
20848     // these go here, so the translation tool can replace them..
20849     this.errors = {
20850         PwdEmpty: "Please type a password, and then retype it to confirm.",
20851         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20852         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20853         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20854         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20855         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20856         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20857         TooWeak: "Your password is Too Weak."
20858     },
20859     this.meterLabel = "Password strength:";
20860     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20861     this.meterClass = [
20862         "roo-password-meter-tooweak", 
20863         "roo-password-meter-weak", 
20864         "roo-password-meter-medium", 
20865         "roo-password-meter-strong", 
20866         "roo-password-meter-grey"
20867     ];
20868     
20869     this.errors = {};
20870     
20871     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20872 }
20873
20874 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20875     /**
20876      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20877      * {
20878      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20879      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20880      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20881      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20882      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20883      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20884      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20885      * })
20886      */
20887     // private
20888     
20889     meterWidth: 300,
20890     errorMsg :'',    
20891     errors: false,
20892     imageRoot: '/',
20893     /**
20894      * @cfg {String/Object} Label for the strength meter (defaults to
20895      * 'Password strength:')
20896      */
20897     // private
20898     meterLabel: '',
20899     /**
20900      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20901      * ['Weak', 'Medium', 'Strong'])
20902      */
20903     // private    
20904     pwdStrengths: false,    
20905     // private
20906     strength: 0,
20907     // private
20908     _lastPwd: null,
20909     // private
20910     kCapitalLetter: 0,
20911     kSmallLetter: 1,
20912     kDigit: 2,
20913     kPunctuation: 3,
20914     
20915     insecure: false,
20916     // private
20917     initEvents: function ()
20918     {
20919         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20920
20921         if (this.el.is('input[type=password]') && Roo.isSafari) {
20922             this.el.on('keydown', this.SafariOnKeyDown, this);
20923         }
20924
20925         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20926     },
20927     // private
20928     onRender: function (ct, position)
20929     {
20930         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20931         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20932         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20933
20934         this.trigger.createChild({
20935                    cn: [
20936                     {
20937                     //id: 'PwdMeter',
20938                     tag: 'div',
20939                     cls: 'roo-password-meter-grey col-xs-12',
20940                     style: {
20941                         //width: 0,
20942                         //width: this.meterWidth + 'px'                                                
20943                         }
20944                     },
20945                     {                            
20946                          cls: 'roo-password-meter-text'                          
20947                     }
20948                 ]            
20949         });
20950
20951          
20952         if (this.hideTrigger) {
20953             this.trigger.setDisplayed(false);
20954         }
20955         this.setSize(this.width || '', this.height || '');
20956     },
20957     // private
20958     onDestroy: function ()
20959     {
20960         if (this.trigger) {
20961             this.trigger.removeAllListeners();
20962             this.trigger.remove();
20963         }
20964         if (this.wrap) {
20965             this.wrap.remove();
20966         }
20967         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20968     },
20969     // private
20970     checkStrength: function ()
20971     {
20972         var pwd = this.inputEl().getValue();
20973         if (pwd == this._lastPwd) {
20974             return;
20975         }
20976
20977         var strength;
20978         if (this.ClientSideStrongPassword(pwd)) {
20979             strength = 3;
20980         } else if (this.ClientSideMediumPassword(pwd)) {
20981             strength = 2;
20982         } else if (this.ClientSideWeakPassword(pwd)) {
20983             strength = 1;
20984         } else {
20985             strength = 0;
20986         }
20987         
20988         Roo.log('strength1: ' + strength);
20989         
20990         //var pm = this.trigger.child('div/div/div').dom;
20991         var pm = this.trigger.child('div/div');
20992         pm.removeClass(this.meterClass);
20993         pm.addClass(this.meterClass[strength]);
20994                 
20995         
20996         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20997                 
20998         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20999         
21000         this._lastPwd = pwd;
21001     },
21002     reset: function ()
21003     {
21004         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21005         
21006         this._lastPwd = '';
21007         
21008         var pm = this.trigger.child('div/div');
21009         pm.removeClass(this.meterClass);
21010         pm.addClass('roo-password-meter-grey');        
21011         
21012         
21013         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21014         
21015         pt.innerHTML = '';
21016         this.inputEl().dom.type='password';
21017     },
21018     // private
21019     validateValue: function (value)
21020     {
21021         
21022         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21023             return false;
21024         }
21025         if (value.length == 0) {
21026             if (this.allowBlank) {
21027                 this.clearInvalid();
21028                 return true;
21029             }
21030
21031             this.markInvalid(this.errors.PwdEmpty);
21032             this.errorMsg = this.errors.PwdEmpty;
21033             return false;
21034         }
21035         
21036         if(this.insecure){
21037             return true;
21038         }
21039         
21040         if ('[\x21-\x7e]*'.match(value)) {
21041             this.markInvalid(this.errors.PwdBadChar);
21042             this.errorMsg = this.errors.PwdBadChar;
21043             return false;
21044         }
21045         if (value.length < 6) {
21046             this.markInvalid(this.errors.PwdShort);
21047             this.errorMsg = this.errors.PwdShort;
21048             return false;
21049         }
21050         if (value.length > 16) {
21051             this.markInvalid(this.errors.PwdLong);
21052             this.errorMsg = this.errors.PwdLong;
21053             return false;
21054         }
21055         var strength;
21056         if (this.ClientSideStrongPassword(value)) {
21057             strength = 3;
21058         } else if (this.ClientSideMediumPassword(value)) {
21059             strength = 2;
21060         } else if (this.ClientSideWeakPassword(value)) {
21061             strength = 1;
21062         } else {
21063             strength = 0;
21064         }
21065
21066         
21067         if (strength < 2) {
21068             //this.markInvalid(this.errors.TooWeak);
21069             this.errorMsg = this.errors.TooWeak;
21070             //return false;
21071         }
21072         
21073         
21074         console.log('strength2: ' + strength);
21075         
21076         //var pm = this.trigger.child('div/div/div').dom;
21077         
21078         var pm = this.trigger.child('div/div');
21079         pm.removeClass(this.meterClass);
21080         pm.addClass(this.meterClass[strength]);
21081                 
21082         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21083                 
21084         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21085         
21086         this.errorMsg = ''; 
21087         return true;
21088     },
21089     // private
21090     CharacterSetChecks: function (type)
21091     {
21092         this.type = type;
21093         this.fResult = false;
21094     },
21095     // private
21096     isctype: function (character, type)
21097     {
21098         switch (type) {  
21099             case this.kCapitalLetter:
21100                 if (character >= 'A' && character <= 'Z') {
21101                     return true;
21102                 }
21103                 break;
21104             
21105             case this.kSmallLetter:
21106                 if (character >= 'a' && character <= 'z') {
21107                     return true;
21108                 }
21109                 break;
21110             
21111             case this.kDigit:
21112                 if (character >= '0' && character <= '9') {
21113                     return true;
21114                 }
21115                 break;
21116             
21117             case this.kPunctuation:
21118                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21119                     return true;
21120                 }
21121                 break;
21122             
21123             default:
21124                 return false;
21125         }
21126
21127     },
21128     // private
21129     IsLongEnough: function (pwd, size)
21130     {
21131         return !(pwd == null || isNaN(size) || pwd.length < size);
21132     },
21133     // private
21134     SpansEnoughCharacterSets: function (word, nb)
21135     {
21136         if (!this.IsLongEnough(word, nb))
21137         {
21138             return false;
21139         }
21140
21141         var characterSetChecks = new Array(
21142             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21143             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21144         );
21145         
21146         for (var index = 0; index < word.length; ++index) {
21147             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21148                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21149                     characterSetChecks[nCharSet].fResult = true;
21150                     break;
21151                 }
21152             }
21153         }
21154
21155         var nCharSets = 0;
21156         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21157             if (characterSetChecks[nCharSet].fResult) {
21158                 ++nCharSets;
21159             }
21160         }
21161
21162         if (nCharSets < nb) {
21163             return false;
21164         }
21165         return true;
21166     },
21167     // private
21168     ClientSideStrongPassword: function (pwd)
21169     {
21170         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21171     },
21172     // private
21173     ClientSideMediumPassword: function (pwd)
21174     {
21175         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21176     },
21177     // private
21178     ClientSideWeakPassword: function (pwd)
21179     {
21180         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21181     }
21182           
21183 })//<script type="text/javascript">
21184
21185 /*
21186  * Based  Ext JS Library 1.1.1
21187  * Copyright(c) 2006-2007, Ext JS, LLC.
21188  * LGPL
21189  *
21190  */
21191  
21192 /**
21193  * @class Roo.HtmlEditorCore
21194  * @extends Roo.Component
21195  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21196  *
21197  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21198  */
21199
21200 Roo.HtmlEditorCore = function(config){
21201     
21202     
21203     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21204     
21205     
21206     this.addEvents({
21207         /**
21208          * @event initialize
21209          * Fires when the editor is fully initialized (including the iframe)
21210          * @param {Roo.HtmlEditorCore} this
21211          */
21212         initialize: true,
21213         /**
21214          * @event activate
21215          * Fires when the editor is first receives the focus. Any insertion must wait
21216          * until after this event.
21217          * @param {Roo.HtmlEditorCore} this
21218          */
21219         activate: true,
21220          /**
21221          * @event beforesync
21222          * Fires before the textarea is updated with content from the editor iframe. Return false
21223          * to cancel the sync.
21224          * @param {Roo.HtmlEditorCore} this
21225          * @param {String} html
21226          */
21227         beforesync: true,
21228          /**
21229          * @event beforepush
21230          * Fires before the iframe editor is updated with content from the textarea. Return false
21231          * to cancel the push.
21232          * @param {Roo.HtmlEditorCore} this
21233          * @param {String} html
21234          */
21235         beforepush: true,
21236          /**
21237          * @event sync
21238          * Fires when the textarea is updated with content from the editor iframe.
21239          * @param {Roo.HtmlEditorCore} this
21240          * @param {String} html
21241          */
21242         sync: true,
21243          /**
21244          * @event push
21245          * Fires when the iframe editor is updated with content from the textarea.
21246          * @param {Roo.HtmlEditorCore} this
21247          * @param {String} html
21248          */
21249         push: true,
21250         
21251         /**
21252          * @event editorevent
21253          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21254          * @param {Roo.HtmlEditorCore} this
21255          */
21256         editorevent: true
21257         
21258     });
21259     
21260     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21261     
21262     // defaults : white / black...
21263     this.applyBlacklists();
21264     
21265     
21266     
21267 };
21268
21269
21270 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21271
21272
21273      /**
21274      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21275      */
21276     
21277     owner : false,
21278     
21279      /**
21280      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21281      *                        Roo.resizable.
21282      */
21283     resizable : false,
21284      /**
21285      * @cfg {Number} height (in pixels)
21286      */   
21287     height: 300,
21288    /**
21289      * @cfg {Number} width (in pixels)
21290      */   
21291     width: 500,
21292     
21293     /**
21294      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21295      * 
21296      */
21297     stylesheets: false,
21298     
21299     // id of frame..
21300     frameId: false,
21301     
21302     // private properties
21303     validationEvent : false,
21304     deferHeight: true,
21305     initialized : false,
21306     activated : false,
21307     sourceEditMode : false,
21308     onFocus : Roo.emptyFn,
21309     iframePad:3,
21310     hideMode:'offsets',
21311     
21312     clearUp: true,
21313     
21314     // blacklist + whitelisted elements..
21315     black: false,
21316     white: false,
21317      
21318     bodyCls : '',
21319
21320     /**
21321      * Protected method that will not generally be called directly. It
21322      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21323      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21324      */
21325     getDocMarkup : function(){
21326         // body styles..
21327         var st = '';
21328         
21329         // inherit styels from page...?? 
21330         if (this.stylesheets === false) {
21331             
21332             Roo.get(document.head).select('style').each(function(node) {
21333                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21334             });
21335             
21336             Roo.get(document.head).select('link').each(function(node) { 
21337                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21338             });
21339             
21340         } else if (!this.stylesheets.length) {
21341                 // simple..
21342                 st = '<style type="text/css">' +
21343                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21344                    '</style>';
21345         } else { 
21346             st = '<style type="text/css">' +
21347                     this.stylesheets +
21348                 '</style>';
21349         }
21350         
21351         st +=  '<style type="text/css">' +
21352             'IMG { cursor: pointer } ' +
21353         '</style>';
21354
21355         var cls = 'roo-htmleditor-body';
21356         
21357         if(this.bodyCls.length){
21358             cls += ' ' + this.bodyCls;
21359         }
21360         
21361         return '<html><head>' + st  +
21362             //<style type="text/css">' +
21363             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21364             //'</style>' +
21365             ' </head><body class="' +  cls + '"></body></html>';
21366     },
21367
21368     // private
21369     onRender : function(ct, position)
21370     {
21371         var _t = this;
21372         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21373         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21374         
21375         
21376         this.el.dom.style.border = '0 none';
21377         this.el.dom.setAttribute('tabIndex', -1);
21378         this.el.addClass('x-hidden hide');
21379         
21380         
21381         
21382         if(Roo.isIE){ // fix IE 1px bogus margin
21383             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21384         }
21385        
21386         
21387         this.frameId = Roo.id();
21388         
21389          
21390         
21391         var iframe = this.owner.wrap.createChild({
21392             tag: 'iframe',
21393             cls: 'form-control', // bootstrap..
21394             id: this.frameId,
21395             name: this.frameId,
21396             frameBorder : 'no',
21397             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21398         }, this.el
21399         );
21400         
21401         
21402         this.iframe = iframe.dom;
21403
21404          this.assignDocWin();
21405         
21406         this.doc.designMode = 'on';
21407        
21408         this.doc.open();
21409         this.doc.write(this.getDocMarkup());
21410         this.doc.close();
21411
21412         
21413         var task = { // must defer to wait for browser to be ready
21414             run : function(){
21415                 //console.log("run task?" + this.doc.readyState);
21416                 this.assignDocWin();
21417                 if(this.doc.body || this.doc.readyState == 'complete'){
21418                     try {
21419                         this.doc.designMode="on";
21420                     } catch (e) {
21421                         return;
21422                     }
21423                     Roo.TaskMgr.stop(task);
21424                     this.initEditor.defer(10, this);
21425                 }
21426             },
21427             interval : 10,
21428             duration: 10000,
21429             scope: this
21430         };
21431         Roo.TaskMgr.start(task);
21432
21433     },
21434
21435     // private
21436     onResize : function(w, h)
21437     {
21438          Roo.log('resize: ' +w + ',' + h );
21439         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21440         if(!this.iframe){
21441             return;
21442         }
21443         if(typeof w == 'number'){
21444             
21445             this.iframe.style.width = w + 'px';
21446         }
21447         if(typeof h == 'number'){
21448             
21449             this.iframe.style.height = h + 'px';
21450             if(this.doc){
21451                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21452             }
21453         }
21454         
21455     },
21456
21457     /**
21458      * Toggles the editor between standard and source edit mode.
21459      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21460      */
21461     toggleSourceEdit : function(sourceEditMode){
21462         
21463         this.sourceEditMode = sourceEditMode === true;
21464         
21465         if(this.sourceEditMode){
21466  
21467             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21468             
21469         }else{
21470             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21471             //this.iframe.className = '';
21472             this.deferFocus();
21473         }
21474         //this.setSize(this.owner.wrap.getSize());
21475         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21476     },
21477
21478     
21479   
21480
21481     /**
21482      * Protected method that will not generally be called directly. If you need/want
21483      * custom HTML cleanup, this is the method you should override.
21484      * @param {String} html The HTML to be cleaned
21485      * return {String} The cleaned HTML
21486      */
21487     cleanHtml : function(html){
21488         html = String(html);
21489         if(html.length > 5){
21490             if(Roo.isSafari){ // strip safari nonsense
21491                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21492             }
21493         }
21494         if(html == '&nbsp;'){
21495             html = '';
21496         }
21497         return html;
21498     },
21499
21500     /**
21501      * HTML Editor -> Textarea
21502      * Protected method that will not generally be called directly. Syncs the contents
21503      * of the editor iframe with the textarea.
21504      */
21505     syncValue : function(){
21506         if(this.initialized){
21507             var bd = (this.doc.body || this.doc.documentElement);
21508             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21509             var html = bd.innerHTML;
21510             if(Roo.isSafari){
21511                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21512                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21513                 if(m && m[1]){
21514                     html = '<div style="'+m[0]+'">' + html + '</div>';
21515                 }
21516             }
21517             html = this.cleanHtml(html);
21518             // fix up the special chars.. normaly like back quotes in word...
21519             // however we do not want to do this with chinese..
21520             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21521                 var cc = b.charCodeAt();
21522                 if (
21523                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21524                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21525                     (cc >= 0xf900 && cc < 0xfb00 )
21526                 ) {
21527                         return b;
21528                 }
21529                 return "&#"+cc+";" 
21530             });
21531             if(this.owner.fireEvent('beforesync', this, html) !== false){
21532                 this.el.dom.value = html;
21533                 this.owner.fireEvent('sync', this, html);
21534             }
21535         }
21536     },
21537
21538     /**
21539      * Protected method that will not generally be called directly. Pushes the value of the textarea
21540      * into the iframe editor.
21541      */
21542     pushValue : function(){
21543         if(this.initialized){
21544             var v = this.el.dom.value.trim();
21545             
21546 //            if(v.length < 1){
21547 //                v = '&#160;';
21548 //            }
21549             
21550             if(this.owner.fireEvent('beforepush', this, v) !== false){
21551                 var d = (this.doc.body || this.doc.documentElement);
21552                 d.innerHTML = v;
21553                 this.cleanUpPaste();
21554                 this.el.dom.value = d.innerHTML;
21555                 this.owner.fireEvent('push', this, v);
21556             }
21557         }
21558     },
21559
21560     // private
21561     deferFocus : function(){
21562         this.focus.defer(10, this);
21563     },
21564
21565     // doc'ed in Field
21566     focus : function(){
21567         if(this.win && !this.sourceEditMode){
21568             this.win.focus();
21569         }else{
21570             this.el.focus();
21571         }
21572     },
21573     
21574     assignDocWin: function()
21575     {
21576         var iframe = this.iframe;
21577         
21578          if(Roo.isIE){
21579             this.doc = iframe.contentWindow.document;
21580             this.win = iframe.contentWindow;
21581         } else {
21582 //            if (!Roo.get(this.frameId)) {
21583 //                return;
21584 //            }
21585 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21586 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21587             
21588             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21589                 return;
21590             }
21591             
21592             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21593             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21594         }
21595     },
21596     
21597     // private
21598     initEditor : function(){
21599         //console.log("INIT EDITOR");
21600         this.assignDocWin();
21601         
21602         
21603         
21604         this.doc.designMode="on";
21605         this.doc.open();
21606         this.doc.write(this.getDocMarkup());
21607         this.doc.close();
21608         
21609         var dbody = (this.doc.body || this.doc.documentElement);
21610         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21611         // this copies styles from the containing element into thsi one..
21612         // not sure why we need all of this..
21613         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21614         
21615         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21616         //ss['background-attachment'] = 'fixed'; // w3c
21617         dbody.bgProperties = 'fixed'; // ie
21618         //Roo.DomHelper.applyStyles(dbody, ss);
21619         Roo.EventManager.on(this.doc, {
21620             //'mousedown': this.onEditorEvent,
21621             'mouseup': this.onEditorEvent,
21622             'dblclick': this.onEditorEvent,
21623             'click': this.onEditorEvent,
21624             'keyup': this.onEditorEvent,
21625             buffer:100,
21626             scope: this
21627         });
21628         if(Roo.isGecko){
21629             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21630         }
21631         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21632             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21633         }
21634         this.initialized = true;
21635
21636         this.owner.fireEvent('initialize', this);
21637         this.pushValue();
21638     },
21639
21640     // private
21641     onDestroy : function(){
21642         
21643         
21644         
21645         if(this.rendered){
21646             
21647             //for (var i =0; i < this.toolbars.length;i++) {
21648             //    // fixme - ask toolbars for heights?
21649             //    this.toolbars[i].onDestroy();
21650            // }
21651             
21652             //this.wrap.dom.innerHTML = '';
21653             //this.wrap.remove();
21654         }
21655     },
21656
21657     // private
21658     onFirstFocus : function(){
21659         
21660         this.assignDocWin();
21661         
21662         
21663         this.activated = true;
21664          
21665     
21666         if(Roo.isGecko){ // prevent silly gecko errors
21667             this.win.focus();
21668             var s = this.win.getSelection();
21669             if(!s.focusNode || s.focusNode.nodeType != 3){
21670                 var r = s.getRangeAt(0);
21671                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21672                 r.collapse(true);
21673                 this.deferFocus();
21674             }
21675             try{
21676                 this.execCmd('useCSS', true);
21677                 this.execCmd('styleWithCSS', false);
21678             }catch(e){}
21679         }
21680         this.owner.fireEvent('activate', this);
21681     },
21682
21683     // private
21684     adjustFont: function(btn){
21685         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21686         //if(Roo.isSafari){ // safari
21687         //    adjust *= 2;
21688        // }
21689         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21690         if(Roo.isSafari){ // safari
21691             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21692             v =  (v < 10) ? 10 : v;
21693             v =  (v > 48) ? 48 : v;
21694             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21695             
21696         }
21697         
21698         
21699         v = Math.max(1, v+adjust);
21700         
21701         this.execCmd('FontSize', v  );
21702     },
21703
21704     onEditorEvent : function(e)
21705     {
21706         this.owner.fireEvent('editorevent', this, e);
21707       //  this.updateToolbar();
21708         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21709     },
21710
21711     insertTag : function(tg)
21712     {
21713         // could be a bit smarter... -> wrap the current selected tRoo..
21714         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21715             
21716             range = this.createRange(this.getSelection());
21717             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21718             wrappingNode.appendChild(range.extractContents());
21719             range.insertNode(wrappingNode);
21720
21721             return;
21722             
21723             
21724             
21725         }
21726         this.execCmd("formatblock",   tg);
21727         
21728     },
21729     
21730     insertText : function(txt)
21731     {
21732         
21733         
21734         var range = this.createRange();
21735         range.deleteContents();
21736                //alert(Sender.getAttribute('label'));
21737                
21738         range.insertNode(this.doc.createTextNode(txt));
21739     } ,
21740     
21741      
21742
21743     /**
21744      * Executes a Midas editor command on the editor document and performs necessary focus and
21745      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21746      * @param {String} cmd The Midas command
21747      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21748      */
21749     relayCmd : function(cmd, value){
21750         this.win.focus();
21751         this.execCmd(cmd, value);
21752         this.owner.fireEvent('editorevent', this);
21753         //this.updateToolbar();
21754         this.owner.deferFocus();
21755     },
21756
21757     /**
21758      * Executes a Midas editor command directly on the editor document.
21759      * For visual commands, you should use {@link #relayCmd} instead.
21760      * <b>This should only be called after the editor is initialized.</b>
21761      * @param {String} cmd The Midas command
21762      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21763      */
21764     execCmd : function(cmd, value){
21765         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21766         this.syncValue();
21767     },
21768  
21769  
21770    
21771     /**
21772      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21773      * to insert tRoo.
21774      * @param {String} text | dom node.. 
21775      */
21776     insertAtCursor : function(text)
21777     {
21778         
21779         if(!this.activated){
21780             return;
21781         }
21782         /*
21783         if(Roo.isIE){
21784             this.win.focus();
21785             var r = this.doc.selection.createRange();
21786             if(r){
21787                 r.collapse(true);
21788                 r.pasteHTML(text);
21789                 this.syncValue();
21790                 this.deferFocus();
21791             
21792             }
21793             return;
21794         }
21795         */
21796         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21797             this.win.focus();
21798             
21799             
21800             // from jquery ui (MIT licenced)
21801             var range, node;
21802             var win = this.win;
21803             
21804             if (win.getSelection && win.getSelection().getRangeAt) {
21805                 range = win.getSelection().getRangeAt(0);
21806                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21807                 range.insertNode(node);
21808             } else if (win.document.selection && win.document.selection.createRange) {
21809                 // no firefox support
21810                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21811                 win.document.selection.createRange().pasteHTML(txt);
21812             } else {
21813                 // no firefox support
21814                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21815                 this.execCmd('InsertHTML', txt);
21816             } 
21817             
21818             this.syncValue();
21819             
21820             this.deferFocus();
21821         }
21822     },
21823  // private
21824     mozKeyPress : function(e){
21825         if(e.ctrlKey){
21826             var c = e.getCharCode(), cmd;
21827           
21828             if(c > 0){
21829                 c = String.fromCharCode(c).toLowerCase();
21830                 switch(c){
21831                     case 'b':
21832                         cmd = 'bold';
21833                         break;
21834                     case 'i':
21835                         cmd = 'italic';
21836                         break;
21837                     
21838                     case 'u':
21839                         cmd = 'underline';
21840                         break;
21841                     
21842                     case 'v':
21843                         this.cleanUpPaste.defer(100, this);
21844                         return;
21845                         
21846                 }
21847                 if(cmd){
21848                     this.win.focus();
21849                     this.execCmd(cmd);
21850                     this.deferFocus();
21851                     e.preventDefault();
21852                 }
21853                 
21854             }
21855         }
21856     },
21857
21858     // private
21859     fixKeys : function(){ // load time branching for fastest keydown performance
21860         if(Roo.isIE){
21861             return function(e){
21862                 var k = e.getKey(), r;
21863                 if(k == e.TAB){
21864                     e.stopEvent();
21865                     r = this.doc.selection.createRange();
21866                     if(r){
21867                         r.collapse(true);
21868                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21869                         this.deferFocus();
21870                     }
21871                     return;
21872                 }
21873                 
21874                 if(k == e.ENTER){
21875                     r = this.doc.selection.createRange();
21876                     if(r){
21877                         var target = r.parentElement();
21878                         if(!target || target.tagName.toLowerCase() != 'li'){
21879                             e.stopEvent();
21880                             r.pasteHTML('<br />');
21881                             r.collapse(false);
21882                             r.select();
21883                         }
21884                     }
21885                 }
21886                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21887                     this.cleanUpPaste.defer(100, this);
21888                     return;
21889                 }
21890                 
21891                 
21892             };
21893         }else if(Roo.isOpera){
21894             return function(e){
21895                 var k = e.getKey();
21896                 if(k == e.TAB){
21897                     e.stopEvent();
21898                     this.win.focus();
21899                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21900                     this.deferFocus();
21901                 }
21902                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21903                     this.cleanUpPaste.defer(100, this);
21904                     return;
21905                 }
21906                 
21907             };
21908         }else if(Roo.isSafari){
21909             return function(e){
21910                 var k = e.getKey();
21911                 
21912                 if(k == e.TAB){
21913                     e.stopEvent();
21914                     this.execCmd('InsertText','\t');
21915                     this.deferFocus();
21916                     return;
21917                 }
21918                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21919                     this.cleanUpPaste.defer(100, this);
21920                     return;
21921                 }
21922                 
21923              };
21924         }
21925     }(),
21926     
21927     getAllAncestors: function()
21928     {
21929         var p = this.getSelectedNode();
21930         var a = [];
21931         if (!p) {
21932             a.push(p); // push blank onto stack..
21933             p = this.getParentElement();
21934         }
21935         
21936         
21937         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21938             a.push(p);
21939             p = p.parentNode;
21940         }
21941         a.push(this.doc.body);
21942         return a;
21943     },
21944     lastSel : false,
21945     lastSelNode : false,
21946     
21947     
21948     getSelection : function() 
21949     {
21950         this.assignDocWin();
21951         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21952     },
21953     
21954     getSelectedNode: function() 
21955     {
21956         // this may only work on Gecko!!!
21957         
21958         // should we cache this!!!!
21959         
21960         
21961         
21962          
21963         var range = this.createRange(this.getSelection()).cloneRange();
21964         
21965         if (Roo.isIE) {
21966             var parent = range.parentElement();
21967             while (true) {
21968                 var testRange = range.duplicate();
21969                 testRange.moveToElementText(parent);
21970                 if (testRange.inRange(range)) {
21971                     break;
21972                 }
21973                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21974                     break;
21975                 }
21976                 parent = parent.parentElement;
21977             }
21978             return parent;
21979         }
21980         
21981         // is ancestor a text element.
21982         var ac =  range.commonAncestorContainer;
21983         if (ac.nodeType == 3) {
21984             ac = ac.parentNode;
21985         }
21986         
21987         var ar = ac.childNodes;
21988          
21989         var nodes = [];
21990         var other_nodes = [];
21991         var has_other_nodes = false;
21992         for (var i=0;i<ar.length;i++) {
21993             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21994                 continue;
21995             }
21996             // fullly contained node.
21997             
21998             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21999                 nodes.push(ar[i]);
22000                 continue;
22001             }
22002             
22003             // probably selected..
22004             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22005                 other_nodes.push(ar[i]);
22006                 continue;
22007             }
22008             // outer..
22009             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22010                 continue;
22011             }
22012             
22013             
22014             has_other_nodes = true;
22015         }
22016         if (!nodes.length && other_nodes.length) {
22017             nodes= other_nodes;
22018         }
22019         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22020             return false;
22021         }
22022         
22023         return nodes[0];
22024     },
22025     createRange: function(sel)
22026     {
22027         // this has strange effects when using with 
22028         // top toolbar - not sure if it's a great idea.
22029         //this.editor.contentWindow.focus();
22030         if (typeof sel != "undefined") {
22031             try {
22032                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22033             } catch(e) {
22034                 return this.doc.createRange();
22035             }
22036         } else {
22037             return this.doc.createRange();
22038         }
22039     },
22040     getParentElement: function()
22041     {
22042         
22043         this.assignDocWin();
22044         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22045         
22046         var range = this.createRange(sel);
22047          
22048         try {
22049             var p = range.commonAncestorContainer;
22050             while (p.nodeType == 3) { // text node
22051                 p = p.parentNode;
22052             }
22053             return p;
22054         } catch (e) {
22055             return null;
22056         }
22057     
22058     },
22059     /***
22060      *
22061      * Range intersection.. the hard stuff...
22062      *  '-1' = before
22063      *  '0' = hits..
22064      *  '1' = after.
22065      *         [ -- selected range --- ]
22066      *   [fail]                        [fail]
22067      *
22068      *    basically..
22069      *      if end is before start or  hits it. fail.
22070      *      if start is after end or hits it fail.
22071      *
22072      *   if either hits (but other is outside. - then it's not 
22073      *   
22074      *    
22075      **/
22076     
22077     
22078     // @see http://www.thismuchiknow.co.uk/?p=64.
22079     rangeIntersectsNode : function(range, node)
22080     {
22081         var nodeRange = node.ownerDocument.createRange();
22082         try {
22083             nodeRange.selectNode(node);
22084         } catch (e) {
22085             nodeRange.selectNodeContents(node);
22086         }
22087     
22088         var rangeStartRange = range.cloneRange();
22089         rangeStartRange.collapse(true);
22090     
22091         var rangeEndRange = range.cloneRange();
22092         rangeEndRange.collapse(false);
22093     
22094         var nodeStartRange = nodeRange.cloneRange();
22095         nodeStartRange.collapse(true);
22096     
22097         var nodeEndRange = nodeRange.cloneRange();
22098         nodeEndRange.collapse(false);
22099     
22100         return rangeStartRange.compareBoundaryPoints(
22101                  Range.START_TO_START, nodeEndRange) == -1 &&
22102                rangeEndRange.compareBoundaryPoints(
22103                  Range.START_TO_START, nodeStartRange) == 1;
22104         
22105          
22106     },
22107     rangeCompareNode : function(range, node)
22108     {
22109         var nodeRange = node.ownerDocument.createRange();
22110         try {
22111             nodeRange.selectNode(node);
22112         } catch (e) {
22113             nodeRange.selectNodeContents(node);
22114         }
22115         
22116         
22117         range.collapse(true);
22118     
22119         nodeRange.collapse(true);
22120      
22121         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22122         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22123          
22124         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22125         
22126         var nodeIsBefore   =  ss == 1;
22127         var nodeIsAfter    = ee == -1;
22128         
22129         if (nodeIsBefore && nodeIsAfter) {
22130             return 0; // outer
22131         }
22132         if (!nodeIsBefore && nodeIsAfter) {
22133             return 1; //right trailed.
22134         }
22135         
22136         if (nodeIsBefore && !nodeIsAfter) {
22137             return 2;  // left trailed.
22138         }
22139         // fully contined.
22140         return 3;
22141     },
22142
22143     // private? - in a new class?
22144     cleanUpPaste :  function()
22145     {
22146         // cleans up the whole document..
22147         Roo.log('cleanuppaste');
22148         
22149         this.cleanUpChildren(this.doc.body);
22150         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22151         if (clean != this.doc.body.innerHTML) {
22152             this.doc.body.innerHTML = clean;
22153         }
22154         
22155     },
22156     
22157     cleanWordChars : function(input) {// change the chars to hex code
22158         var he = Roo.HtmlEditorCore;
22159         
22160         var output = input;
22161         Roo.each(he.swapCodes, function(sw) { 
22162             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22163             
22164             output = output.replace(swapper, sw[1]);
22165         });
22166         
22167         return output;
22168     },
22169     
22170     
22171     cleanUpChildren : function (n)
22172     {
22173         if (!n.childNodes.length) {
22174             return;
22175         }
22176         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22177            this.cleanUpChild(n.childNodes[i]);
22178         }
22179     },
22180     
22181     
22182         
22183     
22184     cleanUpChild : function (node)
22185     {
22186         var ed = this;
22187         //console.log(node);
22188         if (node.nodeName == "#text") {
22189             // clean up silly Windows -- stuff?
22190             return; 
22191         }
22192         if (node.nodeName == "#comment") {
22193             node.parentNode.removeChild(node);
22194             // clean up silly Windows -- stuff?
22195             return; 
22196         }
22197         var lcname = node.tagName.toLowerCase();
22198         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22199         // whitelist of tags..
22200         
22201         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22202             // remove node.
22203             node.parentNode.removeChild(node);
22204             return;
22205             
22206         }
22207         
22208         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22209         
22210         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22211         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22212         
22213         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22214         //    remove_keep_children = true;
22215         //}
22216         
22217         if (remove_keep_children) {
22218             this.cleanUpChildren(node);
22219             // inserts everything just before this node...
22220             while (node.childNodes.length) {
22221                 var cn = node.childNodes[0];
22222                 node.removeChild(cn);
22223                 node.parentNode.insertBefore(cn, node);
22224             }
22225             node.parentNode.removeChild(node);
22226             return;
22227         }
22228         
22229         if (!node.attributes || !node.attributes.length) {
22230             this.cleanUpChildren(node);
22231             return;
22232         }
22233         
22234         function cleanAttr(n,v)
22235         {
22236             
22237             if (v.match(/^\./) || v.match(/^\//)) {
22238                 return;
22239             }
22240             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22241                 return;
22242             }
22243             if (v.match(/^#/)) {
22244                 return;
22245             }
22246 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22247             node.removeAttribute(n);
22248             
22249         }
22250         
22251         var cwhite = this.cwhite;
22252         var cblack = this.cblack;
22253             
22254         function cleanStyle(n,v)
22255         {
22256             if (v.match(/expression/)) { //XSS?? should we even bother..
22257                 node.removeAttribute(n);
22258                 return;
22259             }
22260             
22261             var parts = v.split(/;/);
22262             var clean = [];
22263             
22264             Roo.each(parts, function(p) {
22265                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22266                 if (!p.length) {
22267                     return true;
22268                 }
22269                 var l = p.split(':').shift().replace(/\s+/g,'');
22270                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22271                 
22272                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22273 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22274                     //node.removeAttribute(n);
22275                     return true;
22276                 }
22277                 //Roo.log()
22278                 // only allow 'c whitelisted system attributes'
22279                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22280 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22281                     //node.removeAttribute(n);
22282                     return true;
22283                 }
22284                 
22285                 
22286                  
22287                 
22288                 clean.push(p);
22289                 return true;
22290             });
22291             if (clean.length) { 
22292                 node.setAttribute(n, clean.join(';'));
22293             } else {
22294                 node.removeAttribute(n);
22295             }
22296             
22297         }
22298         
22299         
22300         for (var i = node.attributes.length-1; i > -1 ; i--) {
22301             var a = node.attributes[i];
22302             //console.log(a);
22303             
22304             if (a.name.toLowerCase().substr(0,2)=='on')  {
22305                 node.removeAttribute(a.name);
22306                 continue;
22307             }
22308             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22309                 node.removeAttribute(a.name);
22310                 continue;
22311             }
22312             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22313                 cleanAttr(a.name,a.value); // fixme..
22314                 continue;
22315             }
22316             if (a.name == 'style') {
22317                 cleanStyle(a.name,a.value);
22318                 continue;
22319             }
22320             /// clean up MS crap..
22321             // tecnically this should be a list of valid class'es..
22322             
22323             
22324             if (a.name == 'class') {
22325                 if (a.value.match(/^Mso/)) {
22326                     node.className = '';
22327                 }
22328                 
22329                 if (a.value.match(/^body$/)) {
22330                     node.className = '';
22331                 }
22332                 continue;
22333             }
22334             
22335             // style cleanup!?
22336             // class cleanup?
22337             
22338         }
22339         
22340         
22341         this.cleanUpChildren(node);
22342         
22343         
22344     },
22345     
22346     /**
22347      * Clean up MS wordisms...
22348      */
22349     cleanWord : function(node)
22350     {
22351         
22352         
22353         if (!node) {
22354             this.cleanWord(this.doc.body);
22355             return;
22356         }
22357         if (node.nodeName == "#text") {
22358             // clean up silly Windows -- stuff?
22359             return; 
22360         }
22361         if (node.nodeName == "#comment") {
22362             node.parentNode.removeChild(node);
22363             // clean up silly Windows -- stuff?
22364             return; 
22365         }
22366         
22367         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22368             node.parentNode.removeChild(node);
22369             return;
22370         }
22371         
22372         // remove - but keep children..
22373         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22374             while (node.childNodes.length) {
22375                 var cn = node.childNodes[0];
22376                 node.removeChild(cn);
22377                 node.parentNode.insertBefore(cn, node);
22378             }
22379             node.parentNode.removeChild(node);
22380             this.iterateChildren(node, this.cleanWord);
22381             return;
22382         }
22383         // clean styles
22384         if (node.className.length) {
22385             
22386             var cn = node.className.split(/\W+/);
22387             var cna = [];
22388             Roo.each(cn, function(cls) {
22389                 if (cls.match(/Mso[a-zA-Z]+/)) {
22390                     return;
22391                 }
22392                 cna.push(cls);
22393             });
22394             node.className = cna.length ? cna.join(' ') : '';
22395             if (!cna.length) {
22396                 node.removeAttribute("class");
22397             }
22398         }
22399         
22400         if (node.hasAttribute("lang")) {
22401             node.removeAttribute("lang");
22402         }
22403         
22404         if (node.hasAttribute("style")) {
22405             
22406             var styles = node.getAttribute("style").split(";");
22407             var nstyle = [];
22408             Roo.each(styles, function(s) {
22409                 if (!s.match(/:/)) {
22410                     return;
22411                 }
22412                 var kv = s.split(":");
22413                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22414                     return;
22415                 }
22416                 // what ever is left... we allow.
22417                 nstyle.push(s);
22418             });
22419             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22420             if (!nstyle.length) {
22421                 node.removeAttribute('style');
22422             }
22423         }
22424         this.iterateChildren(node, this.cleanWord);
22425         
22426         
22427         
22428     },
22429     /**
22430      * iterateChildren of a Node, calling fn each time, using this as the scole..
22431      * @param {DomNode} node node to iterate children of.
22432      * @param {Function} fn method of this class to call on each item.
22433      */
22434     iterateChildren : function(node, fn)
22435     {
22436         if (!node.childNodes.length) {
22437                 return;
22438         }
22439         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22440            fn.call(this, node.childNodes[i])
22441         }
22442     },
22443     
22444     
22445     /**
22446      * cleanTableWidths.
22447      *
22448      * Quite often pasting from word etc.. results in tables with column and widths.
22449      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22450      *
22451      */
22452     cleanTableWidths : function(node)
22453     {
22454          
22455          
22456         if (!node) {
22457             this.cleanTableWidths(this.doc.body);
22458             return;
22459         }
22460         
22461         // ignore list...
22462         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22463             return; 
22464         }
22465         Roo.log(node.tagName);
22466         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22467             this.iterateChildren(node, this.cleanTableWidths);
22468             return;
22469         }
22470         if (node.hasAttribute('width')) {
22471             node.removeAttribute('width');
22472         }
22473         
22474          
22475         if (node.hasAttribute("style")) {
22476             // pretty basic...
22477             
22478             var styles = node.getAttribute("style").split(";");
22479             var nstyle = [];
22480             Roo.each(styles, function(s) {
22481                 if (!s.match(/:/)) {
22482                     return;
22483                 }
22484                 var kv = s.split(":");
22485                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22486                     return;
22487                 }
22488                 // what ever is left... we allow.
22489                 nstyle.push(s);
22490             });
22491             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22492             if (!nstyle.length) {
22493                 node.removeAttribute('style');
22494             }
22495         }
22496         
22497         this.iterateChildren(node, this.cleanTableWidths);
22498         
22499         
22500     },
22501     
22502     
22503     
22504     
22505     domToHTML : function(currentElement, depth, nopadtext) {
22506         
22507         depth = depth || 0;
22508         nopadtext = nopadtext || false;
22509     
22510         if (!currentElement) {
22511             return this.domToHTML(this.doc.body);
22512         }
22513         
22514         //Roo.log(currentElement);
22515         var j;
22516         var allText = false;
22517         var nodeName = currentElement.nodeName;
22518         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22519         
22520         if  (nodeName == '#text') {
22521             
22522             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22523         }
22524         
22525         
22526         var ret = '';
22527         if (nodeName != 'BODY') {
22528              
22529             var i = 0;
22530             // Prints the node tagName, such as <A>, <IMG>, etc
22531             if (tagName) {
22532                 var attr = [];
22533                 for(i = 0; i < currentElement.attributes.length;i++) {
22534                     // quoting?
22535                     var aname = currentElement.attributes.item(i).name;
22536                     if (!currentElement.attributes.item(i).value.length) {
22537                         continue;
22538                     }
22539                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22540                 }
22541                 
22542                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22543             } 
22544             else {
22545                 
22546                 // eack
22547             }
22548         } else {
22549             tagName = false;
22550         }
22551         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22552             return ret;
22553         }
22554         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22555             nopadtext = true;
22556         }
22557         
22558         
22559         // Traverse the tree
22560         i = 0;
22561         var currentElementChild = currentElement.childNodes.item(i);
22562         var allText = true;
22563         var innerHTML  = '';
22564         lastnode = '';
22565         while (currentElementChild) {
22566             // Formatting code (indent the tree so it looks nice on the screen)
22567             var nopad = nopadtext;
22568             if (lastnode == 'SPAN') {
22569                 nopad  = true;
22570             }
22571             // text
22572             if  (currentElementChild.nodeName == '#text') {
22573                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22574                 toadd = nopadtext ? toadd : toadd.trim();
22575                 if (!nopad && toadd.length > 80) {
22576                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22577                 }
22578                 innerHTML  += toadd;
22579                 
22580                 i++;
22581                 currentElementChild = currentElement.childNodes.item(i);
22582                 lastNode = '';
22583                 continue;
22584             }
22585             allText = false;
22586             
22587             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22588                 
22589             // Recursively traverse the tree structure of the child node
22590             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22591             lastnode = currentElementChild.nodeName;
22592             i++;
22593             currentElementChild=currentElement.childNodes.item(i);
22594         }
22595         
22596         ret += innerHTML;
22597         
22598         if (!allText) {
22599                 // The remaining code is mostly for formatting the tree
22600             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22601         }
22602         
22603         
22604         if (tagName) {
22605             ret+= "</"+tagName+">";
22606         }
22607         return ret;
22608         
22609     },
22610         
22611     applyBlacklists : function()
22612     {
22613         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22614         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22615         
22616         this.white = [];
22617         this.black = [];
22618         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22619             if (b.indexOf(tag) > -1) {
22620                 return;
22621             }
22622             this.white.push(tag);
22623             
22624         }, this);
22625         
22626         Roo.each(w, function(tag) {
22627             if (b.indexOf(tag) > -1) {
22628                 return;
22629             }
22630             if (this.white.indexOf(tag) > -1) {
22631                 return;
22632             }
22633             this.white.push(tag);
22634             
22635         }, this);
22636         
22637         
22638         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22639             if (w.indexOf(tag) > -1) {
22640                 return;
22641             }
22642             this.black.push(tag);
22643             
22644         }, this);
22645         
22646         Roo.each(b, function(tag) {
22647             if (w.indexOf(tag) > -1) {
22648                 return;
22649             }
22650             if (this.black.indexOf(tag) > -1) {
22651                 return;
22652             }
22653             this.black.push(tag);
22654             
22655         }, this);
22656         
22657         
22658         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22659         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22660         
22661         this.cwhite = [];
22662         this.cblack = [];
22663         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22664             if (b.indexOf(tag) > -1) {
22665                 return;
22666             }
22667             this.cwhite.push(tag);
22668             
22669         }, this);
22670         
22671         Roo.each(w, function(tag) {
22672             if (b.indexOf(tag) > -1) {
22673                 return;
22674             }
22675             if (this.cwhite.indexOf(tag) > -1) {
22676                 return;
22677             }
22678             this.cwhite.push(tag);
22679             
22680         }, this);
22681         
22682         
22683         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22684             if (w.indexOf(tag) > -1) {
22685                 return;
22686             }
22687             this.cblack.push(tag);
22688             
22689         }, this);
22690         
22691         Roo.each(b, function(tag) {
22692             if (w.indexOf(tag) > -1) {
22693                 return;
22694             }
22695             if (this.cblack.indexOf(tag) > -1) {
22696                 return;
22697             }
22698             this.cblack.push(tag);
22699             
22700         }, this);
22701     },
22702     
22703     setStylesheets : function(stylesheets)
22704     {
22705         if(typeof(stylesheets) == 'string'){
22706             Roo.get(this.iframe.contentDocument.head).createChild({
22707                 tag : 'link',
22708                 rel : 'stylesheet',
22709                 type : 'text/css',
22710                 href : stylesheets
22711             });
22712             
22713             return;
22714         }
22715         var _this = this;
22716      
22717         Roo.each(stylesheets, function(s) {
22718             if(!s.length){
22719                 return;
22720             }
22721             
22722             Roo.get(_this.iframe.contentDocument.head).createChild({
22723                 tag : 'link',
22724                 rel : 'stylesheet',
22725                 type : 'text/css',
22726                 href : s
22727             });
22728         });
22729
22730         
22731     },
22732     
22733     removeStylesheets : function()
22734     {
22735         var _this = this;
22736         
22737         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22738             s.remove();
22739         });
22740     },
22741     
22742     setStyle : function(style)
22743     {
22744         Roo.get(this.iframe.contentDocument.head).createChild({
22745             tag : 'style',
22746             type : 'text/css',
22747             html : style
22748         });
22749
22750         return;
22751     }
22752     
22753     // hide stuff that is not compatible
22754     /**
22755      * @event blur
22756      * @hide
22757      */
22758     /**
22759      * @event change
22760      * @hide
22761      */
22762     /**
22763      * @event focus
22764      * @hide
22765      */
22766     /**
22767      * @event specialkey
22768      * @hide
22769      */
22770     /**
22771      * @cfg {String} fieldClass @hide
22772      */
22773     /**
22774      * @cfg {String} focusClass @hide
22775      */
22776     /**
22777      * @cfg {String} autoCreate @hide
22778      */
22779     /**
22780      * @cfg {String} inputType @hide
22781      */
22782     /**
22783      * @cfg {String} invalidClass @hide
22784      */
22785     /**
22786      * @cfg {String} invalidText @hide
22787      */
22788     /**
22789      * @cfg {String} msgFx @hide
22790      */
22791     /**
22792      * @cfg {String} validateOnBlur @hide
22793      */
22794 });
22795
22796 Roo.HtmlEditorCore.white = [
22797         'area', 'br', 'img', 'input', 'hr', 'wbr',
22798         
22799        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22800        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22801        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22802        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22803        'table',   'ul',         'xmp', 
22804        
22805        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22806       'thead',   'tr', 
22807      
22808       'dir', 'menu', 'ol', 'ul', 'dl',
22809        
22810       'embed',  'object'
22811 ];
22812
22813
22814 Roo.HtmlEditorCore.black = [
22815     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22816         'applet', // 
22817         'base',   'basefont', 'bgsound', 'blink',  'body', 
22818         'frame',  'frameset', 'head',    'html',   'ilayer', 
22819         'iframe', 'layer',  'link',     'meta',    'object',   
22820         'script', 'style' ,'title',  'xml' // clean later..
22821 ];
22822 Roo.HtmlEditorCore.clean = [
22823     'script', 'style', 'title', 'xml'
22824 ];
22825 Roo.HtmlEditorCore.remove = [
22826     'font'
22827 ];
22828 // attributes..
22829
22830 Roo.HtmlEditorCore.ablack = [
22831     'on'
22832 ];
22833     
22834 Roo.HtmlEditorCore.aclean = [ 
22835     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22836 ];
22837
22838 // protocols..
22839 Roo.HtmlEditorCore.pwhite= [
22840         'http',  'https',  'mailto'
22841 ];
22842
22843 // white listed style attributes.
22844 Roo.HtmlEditorCore.cwhite= [
22845       //  'text-align', /// default is to allow most things..
22846       
22847          
22848 //        'font-size'//??
22849 ];
22850
22851 // black listed style attributes.
22852 Roo.HtmlEditorCore.cblack= [
22853       //  'font-size' -- this can be set by the project 
22854 ];
22855
22856
22857 Roo.HtmlEditorCore.swapCodes   =[ 
22858     [    8211, "--" ], 
22859     [    8212, "--" ], 
22860     [    8216,  "'" ],  
22861     [    8217, "'" ],  
22862     [    8220, '"' ],  
22863     [    8221, '"' ],  
22864     [    8226, "*" ],  
22865     [    8230, "..." ]
22866 ]; 
22867
22868     /*
22869  * - LGPL
22870  *
22871  * HtmlEditor
22872  * 
22873  */
22874
22875 /**
22876  * @class Roo.bootstrap.HtmlEditor
22877  * @extends Roo.bootstrap.TextArea
22878  * Bootstrap HtmlEditor class
22879
22880  * @constructor
22881  * Create a new HtmlEditor
22882  * @param {Object} config The config object
22883  */
22884
22885 Roo.bootstrap.HtmlEditor = function(config){
22886     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22887     if (!this.toolbars) {
22888         this.toolbars = [];
22889     }
22890     
22891     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22892     this.addEvents({
22893             /**
22894              * @event initialize
22895              * Fires when the editor is fully initialized (including the iframe)
22896              * @param {HtmlEditor} this
22897              */
22898             initialize: true,
22899             /**
22900              * @event activate
22901              * Fires when the editor is first receives the focus. Any insertion must wait
22902              * until after this event.
22903              * @param {HtmlEditor} this
22904              */
22905             activate: true,
22906              /**
22907              * @event beforesync
22908              * Fires before the textarea is updated with content from the editor iframe. Return false
22909              * to cancel the sync.
22910              * @param {HtmlEditor} this
22911              * @param {String} html
22912              */
22913             beforesync: true,
22914              /**
22915              * @event beforepush
22916              * Fires before the iframe editor is updated with content from the textarea. Return false
22917              * to cancel the push.
22918              * @param {HtmlEditor} this
22919              * @param {String} html
22920              */
22921             beforepush: true,
22922              /**
22923              * @event sync
22924              * Fires when the textarea is updated with content from the editor iframe.
22925              * @param {HtmlEditor} this
22926              * @param {String} html
22927              */
22928             sync: true,
22929              /**
22930              * @event push
22931              * Fires when the iframe editor is updated with content from the textarea.
22932              * @param {HtmlEditor} this
22933              * @param {String} html
22934              */
22935             push: true,
22936              /**
22937              * @event editmodechange
22938              * Fires when the editor switches edit modes
22939              * @param {HtmlEditor} this
22940              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22941              */
22942             editmodechange: true,
22943             /**
22944              * @event editorevent
22945              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22946              * @param {HtmlEditor} this
22947              */
22948             editorevent: true,
22949             /**
22950              * @event firstfocus
22951              * Fires when on first focus - needed by toolbars..
22952              * @param {HtmlEditor} this
22953              */
22954             firstfocus: true,
22955             /**
22956              * @event autosave
22957              * Auto save the htmlEditor value as a file into Events
22958              * @param {HtmlEditor} this
22959              */
22960             autosave: true,
22961             /**
22962              * @event savedpreview
22963              * preview the saved version of htmlEditor
22964              * @param {HtmlEditor} this
22965              */
22966             savedpreview: true
22967         });
22968 };
22969
22970
22971 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22972     
22973     
22974       /**
22975      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22976      */
22977     toolbars : false,
22978     
22979      /**
22980     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22981     */
22982     btns : [],
22983    
22984      /**
22985      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22986      *                        Roo.resizable.
22987      */
22988     resizable : false,
22989      /**
22990      * @cfg {Number} height (in pixels)
22991      */   
22992     height: 300,
22993    /**
22994      * @cfg {Number} width (in pixels)
22995      */   
22996     width: false,
22997     
22998     /**
22999      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23000      * 
23001      */
23002     stylesheets: false,
23003     
23004     // id of frame..
23005     frameId: false,
23006     
23007     // private properties
23008     validationEvent : false,
23009     deferHeight: true,
23010     initialized : false,
23011     activated : false,
23012     
23013     onFocus : Roo.emptyFn,
23014     iframePad:3,
23015     hideMode:'offsets',
23016     
23017     tbContainer : false,
23018     
23019     bodyCls : '',
23020     
23021     toolbarContainer :function() {
23022         return this.wrap.select('.x-html-editor-tb',true).first();
23023     },
23024
23025     /**
23026      * Protected method that will not generally be called directly. It
23027      * is called when the editor creates its toolbar. Override this method if you need to
23028      * add custom toolbar buttons.
23029      * @param {HtmlEditor} editor
23030      */
23031     createToolbar : function(){
23032         Roo.log('renewing');
23033         Roo.log("create toolbars");
23034         
23035         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23036         this.toolbars[0].render(this.toolbarContainer());
23037         
23038         return;
23039         
23040 //        if (!editor.toolbars || !editor.toolbars.length) {
23041 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23042 //        }
23043 //        
23044 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23045 //            editor.toolbars[i] = Roo.factory(
23046 //                    typeof(editor.toolbars[i]) == 'string' ?
23047 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23048 //                Roo.bootstrap.HtmlEditor);
23049 //            editor.toolbars[i].init(editor);
23050 //        }
23051     },
23052
23053      
23054     // private
23055     onRender : function(ct, position)
23056     {
23057        // Roo.log("Call onRender: " + this.xtype);
23058         var _t = this;
23059         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23060       
23061         this.wrap = this.inputEl().wrap({
23062             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23063         });
23064         
23065         this.editorcore.onRender(ct, position);
23066          
23067         if (this.resizable) {
23068             this.resizeEl = new Roo.Resizable(this.wrap, {
23069                 pinned : true,
23070                 wrap: true,
23071                 dynamic : true,
23072                 minHeight : this.height,
23073                 height: this.height,
23074                 handles : this.resizable,
23075                 width: this.width,
23076                 listeners : {
23077                     resize : function(r, w, h) {
23078                         _t.onResize(w,h); // -something
23079                     }
23080                 }
23081             });
23082             
23083         }
23084         this.createToolbar(this);
23085        
23086         
23087         if(!this.width && this.resizable){
23088             this.setSize(this.wrap.getSize());
23089         }
23090         if (this.resizeEl) {
23091             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23092             // should trigger onReize..
23093         }
23094         
23095     },
23096
23097     // private
23098     onResize : function(w, h)
23099     {
23100         Roo.log('resize: ' +w + ',' + h );
23101         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23102         var ew = false;
23103         var eh = false;
23104         
23105         if(this.inputEl() ){
23106             if(typeof w == 'number'){
23107                 var aw = w - this.wrap.getFrameWidth('lr');
23108                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23109                 ew = aw;
23110             }
23111             if(typeof h == 'number'){
23112                  var tbh = -11;  // fixme it needs to tool bar size!
23113                 for (var i =0; i < this.toolbars.length;i++) {
23114                     // fixme - ask toolbars for heights?
23115                     tbh += this.toolbars[i].el.getHeight();
23116                     //if (this.toolbars[i].footer) {
23117                     //    tbh += this.toolbars[i].footer.el.getHeight();
23118                     //}
23119                 }
23120               
23121                 
23122                 
23123                 
23124                 
23125                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23126                 ah -= 5; // knock a few pixes off for look..
23127                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23128                 var eh = ah;
23129             }
23130         }
23131         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23132         this.editorcore.onResize(ew,eh);
23133         
23134     },
23135
23136     /**
23137      * Toggles the editor between standard and source edit mode.
23138      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23139      */
23140     toggleSourceEdit : function(sourceEditMode)
23141     {
23142         this.editorcore.toggleSourceEdit(sourceEditMode);
23143         
23144         if(this.editorcore.sourceEditMode){
23145             Roo.log('editor - showing textarea');
23146             
23147 //            Roo.log('in');
23148 //            Roo.log(this.syncValue());
23149             this.syncValue();
23150             this.inputEl().removeClass(['hide', 'x-hidden']);
23151             this.inputEl().dom.removeAttribute('tabIndex');
23152             this.inputEl().focus();
23153         }else{
23154             Roo.log('editor - hiding textarea');
23155 //            Roo.log('out')
23156 //            Roo.log(this.pushValue()); 
23157             this.pushValue();
23158             
23159             this.inputEl().addClass(['hide', 'x-hidden']);
23160             this.inputEl().dom.setAttribute('tabIndex', -1);
23161             //this.deferFocus();
23162         }
23163          
23164         if(this.resizable){
23165             this.setSize(this.wrap.getSize());
23166         }
23167         
23168         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23169     },
23170  
23171     // private (for BoxComponent)
23172     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23173
23174     // private (for BoxComponent)
23175     getResizeEl : function(){
23176         return this.wrap;
23177     },
23178
23179     // private (for BoxComponent)
23180     getPositionEl : function(){
23181         return this.wrap;
23182     },
23183
23184     // private
23185     initEvents : function(){
23186         this.originalValue = this.getValue();
23187     },
23188
23189 //    /**
23190 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23191 //     * @method
23192 //     */
23193 //    markInvalid : Roo.emptyFn,
23194 //    /**
23195 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23196 //     * @method
23197 //     */
23198 //    clearInvalid : Roo.emptyFn,
23199
23200     setValue : function(v){
23201         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23202         this.editorcore.pushValue();
23203     },
23204
23205      
23206     // private
23207     deferFocus : function(){
23208         this.focus.defer(10, this);
23209     },
23210
23211     // doc'ed in Field
23212     focus : function(){
23213         this.editorcore.focus();
23214         
23215     },
23216       
23217
23218     // private
23219     onDestroy : function(){
23220         
23221         
23222         
23223         if(this.rendered){
23224             
23225             for (var i =0; i < this.toolbars.length;i++) {
23226                 // fixme - ask toolbars for heights?
23227                 this.toolbars[i].onDestroy();
23228             }
23229             
23230             this.wrap.dom.innerHTML = '';
23231             this.wrap.remove();
23232         }
23233     },
23234
23235     // private
23236     onFirstFocus : function(){
23237         //Roo.log("onFirstFocus");
23238         this.editorcore.onFirstFocus();
23239          for (var i =0; i < this.toolbars.length;i++) {
23240             this.toolbars[i].onFirstFocus();
23241         }
23242         
23243     },
23244     
23245     // private
23246     syncValue : function()
23247     {   
23248         this.editorcore.syncValue();
23249     },
23250     
23251     pushValue : function()
23252     {   
23253         this.editorcore.pushValue();
23254     }
23255      
23256     
23257     // hide stuff that is not compatible
23258     /**
23259      * @event blur
23260      * @hide
23261      */
23262     /**
23263      * @event change
23264      * @hide
23265      */
23266     /**
23267      * @event focus
23268      * @hide
23269      */
23270     /**
23271      * @event specialkey
23272      * @hide
23273      */
23274     /**
23275      * @cfg {String} fieldClass @hide
23276      */
23277     /**
23278      * @cfg {String} focusClass @hide
23279      */
23280     /**
23281      * @cfg {String} autoCreate @hide
23282      */
23283     /**
23284      * @cfg {String} inputType @hide
23285      */
23286     /**
23287      * @cfg {String} invalidClass @hide
23288      */
23289     /**
23290      * @cfg {String} invalidText @hide
23291      */
23292     /**
23293      * @cfg {String} msgFx @hide
23294      */
23295     /**
23296      * @cfg {String} validateOnBlur @hide
23297      */
23298 });
23299  
23300     
23301    
23302    
23303    
23304       
23305 Roo.namespace('Roo.bootstrap.htmleditor');
23306 /**
23307  * @class Roo.bootstrap.HtmlEditorToolbar1
23308  * Basic Toolbar
23309  * 
23310  * Usage:
23311  *
23312  new Roo.bootstrap.HtmlEditor({
23313     ....
23314     toolbars : [
23315         new Roo.bootstrap.HtmlEditorToolbar1({
23316             disable : { fonts: 1 , format: 1, ..., ... , ...],
23317             btns : [ .... ]
23318         })
23319     }
23320      
23321  * 
23322  * @cfg {Object} disable List of elements to disable..
23323  * @cfg {Array} btns List of additional buttons.
23324  * 
23325  * 
23326  * NEEDS Extra CSS? 
23327  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23328  */
23329  
23330 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23331 {
23332     
23333     Roo.apply(this, config);
23334     
23335     // default disabled, based on 'good practice'..
23336     this.disable = this.disable || {};
23337     Roo.applyIf(this.disable, {
23338         fontSize : true,
23339         colors : true,
23340         specialElements : true
23341     });
23342     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23343     
23344     this.editor = config.editor;
23345     this.editorcore = config.editor.editorcore;
23346     
23347     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23348     
23349     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23350     // dont call parent... till later.
23351 }
23352 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23353      
23354     bar : true,
23355     
23356     editor : false,
23357     editorcore : false,
23358     
23359     
23360     formats : [
23361         "p" ,  
23362         "h1","h2","h3","h4","h5","h6", 
23363         "pre", "code", 
23364         "abbr", "acronym", "address", "cite", "samp", "var",
23365         'div','span'
23366     ],
23367     
23368     onRender : function(ct, position)
23369     {
23370        // Roo.log("Call onRender: " + this.xtype);
23371         
23372        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23373        Roo.log(this.el);
23374        this.el.dom.style.marginBottom = '0';
23375        var _this = this;
23376        var editorcore = this.editorcore;
23377        var editor= this.editor;
23378        
23379        var children = [];
23380        var btn = function(id,cmd , toggle, handler, html){
23381        
23382             var  event = toggle ? 'toggle' : 'click';
23383        
23384             var a = {
23385                 size : 'sm',
23386                 xtype: 'Button',
23387                 xns: Roo.bootstrap,
23388                 glyphicon : id,
23389                 cmd : id || cmd,
23390                 enableToggle:toggle !== false,
23391                 html : html || '',
23392                 pressed : toggle ? false : null,
23393                 listeners : {}
23394             };
23395             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23396                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23397             };
23398             children.push(a);
23399             return a;
23400        }
23401        
23402     //    var cb_box = function...
23403         
23404         var style = {
23405                 xtype: 'Button',
23406                 size : 'sm',
23407                 xns: Roo.bootstrap,
23408                 glyphicon : 'font',
23409                 //html : 'submit'
23410                 menu : {
23411                     xtype: 'Menu',
23412                     xns: Roo.bootstrap,
23413                     items:  []
23414                 }
23415         };
23416         Roo.each(this.formats, function(f) {
23417             style.menu.items.push({
23418                 xtype :'MenuItem',
23419                 xns: Roo.bootstrap,
23420                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23421                 tagname : f,
23422                 listeners : {
23423                     click : function()
23424                     {
23425                         editorcore.insertTag(this.tagname);
23426                         editor.focus();
23427                     }
23428                 }
23429                 
23430             });
23431         });
23432         children.push(style);   
23433         
23434         btn('bold',false,true);
23435         btn('italic',false,true);
23436         btn('align-left', 'justifyleft',true);
23437         btn('align-center', 'justifycenter',true);
23438         btn('align-right' , 'justifyright',true);
23439         btn('link', false, false, function(btn) {
23440             //Roo.log("create link?");
23441             var url = prompt(this.createLinkText, this.defaultLinkValue);
23442             if(url && url != 'http:/'+'/'){
23443                 this.editorcore.relayCmd('createlink', url);
23444             }
23445         }),
23446         btn('list','insertunorderedlist',true);
23447         btn('pencil', false,true, function(btn){
23448                 Roo.log(this);
23449                 this.toggleSourceEdit(btn.pressed);
23450         });
23451         
23452         if (this.editor.btns.length > 0) {
23453             for (var i = 0; i<this.editor.btns.length; i++) {
23454                 children.push(this.editor.btns[i]);
23455             }
23456         }
23457         
23458         /*
23459         var cog = {
23460                 xtype: 'Button',
23461                 size : 'sm',
23462                 xns: Roo.bootstrap,
23463                 glyphicon : 'cog',
23464                 //html : 'submit'
23465                 menu : {
23466                     xtype: 'Menu',
23467                     xns: Roo.bootstrap,
23468                     items:  []
23469                 }
23470         };
23471         
23472         cog.menu.items.push({
23473             xtype :'MenuItem',
23474             xns: Roo.bootstrap,
23475             html : Clean styles,
23476             tagname : f,
23477             listeners : {
23478                 click : function()
23479                 {
23480                     editorcore.insertTag(this.tagname);
23481                     editor.focus();
23482                 }
23483             }
23484             
23485         });
23486        */
23487         
23488          
23489        this.xtype = 'NavSimplebar';
23490         
23491         for(var i=0;i< children.length;i++) {
23492             
23493             this.buttons.add(this.addxtypeChild(children[i]));
23494             
23495         }
23496         
23497         editor.on('editorevent', this.updateToolbar, this);
23498     },
23499     onBtnClick : function(id)
23500     {
23501        this.editorcore.relayCmd(id);
23502        this.editorcore.focus();
23503     },
23504     
23505     /**
23506      * Protected method that will not generally be called directly. It triggers
23507      * a toolbar update by reading the markup state of the current selection in the editor.
23508      */
23509     updateToolbar: function(){
23510
23511         if(!this.editorcore.activated){
23512             this.editor.onFirstFocus(); // is this neeed?
23513             return;
23514         }
23515
23516         var btns = this.buttons; 
23517         var doc = this.editorcore.doc;
23518         btns.get('bold').setActive(doc.queryCommandState('bold'));
23519         btns.get('italic').setActive(doc.queryCommandState('italic'));
23520         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23521         
23522         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23523         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23524         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23525         
23526         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23527         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23528          /*
23529         
23530         var ans = this.editorcore.getAllAncestors();
23531         if (this.formatCombo) {
23532             
23533             
23534             var store = this.formatCombo.store;
23535             this.formatCombo.setValue("");
23536             for (var i =0; i < ans.length;i++) {
23537                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23538                     // select it..
23539                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23540                     break;
23541                 }
23542             }
23543         }
23544         
23545         
23546         
23547         // hides menus... - so this cant be on a menu...
23548         Roo.bootstrap.MenuMgr.hideAll();
23549         */
23550         Roo.bootstrap.MenuMgr.hideAll();
23551         //this.editorsyncValue();
23552     },
23553     onFirstFocus: function() {
23554         this.buttons.each(function(item){
23555            item.enable();
23556         });
23557     },
23558     toggleSourceEdit : function(sourceEditMode){
23559         
23560           
23561         if(sourceEditMode){
23562             Roo.log("disabling buttons");
23563            this.buttons.each( function(item){
23564                 if(item.cmd != 'pencil'){
23565                     item.disable();
23566                 }
23567             });
23568           
23569         }else{
23570             Roo.log("enabling buttons");
23571             if(this.editorcore.initialized){
23572                 this.buttons.each( function(item){
23573                     item.enable();
23574                 });
23575             }
23576             
23577         }
23578         Roo.log("calling toggole on editor");
23579         // tell the editor that it's been pressed..
23580         this.editor.toggleSourceEdit(sourceEditMode);
23581        
23582     }
23583 });
23584
23585
23586
23587
23588
23589 /**
23590  * @class Roo.bootstrap.Table.AbstractSelectionModel
23591  * @extends Roo.util.Observable
23592  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23593  * implemented by descendant classes.  This class should not be directly instantiated.
23594  * @constructor
23595  */
23596 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23597     this.locked = false;
23598     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23599 };
23600
23601
23602 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23603     /** @ignore Called by the grid automatically. Do not call directly. */
23604     init : function(grid){
23605         this.grid = grid;
23606         this.initEvents();
23607     },
23608
23609     /**
23610      * Locks the selections.
23611      */
23612     lock : function(){
23613         this.locked = true;
23614     },
23615
23616     /**
23617      * Unlocks the selections.
23618      */
23619     unlock : function(){
23620         this.locked = false;
23621     },
23622
23623     /**
23624      * Returns true if the selections are locked.
23625      * @return {Boolean}
23626      */
23627     isLocked : function(){
23628         return this.locked;
23629     }
23630 });
23631 /**
23632  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23633  * @class Roo.bootstrap.Table.RowSelectionModel
23634  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23635  * It supports multiple selections and keyboard selection/navigation. 
23636  * @constructor
23637  * @param {Object} config
23638  */
23639
23640 Roo.bootstrap.Table.RowSelectionModel = function(config){
23641     Roo.apply(this, config);
23642     this.selections = new Roo.util.MixedCollection(false, function(o){
23643         return o.id;
23644     });
23645
23646     this.last = false;
23647     this.lastActive = false;
23648
23649     this.addEvents({
23650         /**
23651              * @event selectionchange
23652              * Fires when the selection changes
23653              * @param {SelectionModel} this
23654              */
23655             "selectionchange" : true,
23656         /**
23657              * @event afterselectionchange
23658              * Fires after the selection changes (eg. by key press or clicking)
23659              * @param {SelectionModel} this
23660              */
23661             "afterselectionchange" : true,
23662         /**
23663              * @event beforerowselect
23664              * Fires when a row is selected being selected, return false to cancel.
23665              * @param {SelectionModel} this
23666              * @param {Number} rowIndex The selected index
23667              * @param {Boolean} keepExisting False if other selections will be cleared
23668              */
23669             "beforerowselect" : true,
23670         /**
23671              * @event rowselect
23672              * Fires when a row is selected.
23673              * @param {SelectionModel} this
23674              * @param {Number} rowIndex The selected index
23675              * @param {Roo.data.Record} r The record
23676              */
23677             "rowselect" : true,
23678         /**
23679              * @event rowdeselect
23680              * Fires when a row is deselected.
23681              * @param {SelectionModel} this
23682              * @param {Number} rowIndex The selected index
23683              */
23684         "rowdeselect" : true
23685     });
23686     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23687     this.locked = false;
23688  };
23689
23690 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23691     /**
23692      * @cfg {Boolean} singleSelect
23693      * True to allow selection of only one row at a time (defaults to false)
23694      */
23695     singleSelect : false,
23696
23697     // private
23698     initEvents : function()
23699     {
23700
23701         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23702         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23703         //}else{ // allow click to work like normal
23704          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23705         //}
23706         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23707         this.grid.on("rowclick", this.handleMouseDown, this);
23708         
23709         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23710             "up" : function(e){
23711                 if(!e.shiftKey){
23712                     this.selectPrevious(e.shiftKey);
23713                 }else if(this.last !== false && this.lastActive !== false){
23714                     var last = this.last;
23715                     this.selectRange(this.last,  this.lastActive-1);
23716                     this.grid.getView().focusRow(this.lastActive);
23717                     if(last !== false){
23718                         this.last = last;
23719                     }
23720                 }else{
23721                     this.selectFirstRow();
23722                 }
23723                 this.fireEvent("afterselectionchange", this);
23724             },
23725             "down" : function(e){
23726                 if(!e.shiftKey){
23727                     this.selectNext(e.shiftKey);
23728                 }else if(this.last !== false && this.lastActive !== false){
23729                     var last = this.last;
23730                     this.selectRange(this.last,  this.lastActive+1);
23731                     this.grid.getView().focusRow(this.lastActive);
23732                     if(last !== false){
23733                         this.last = last;
23734                     }
23735                 }else{
23736                     this.selectFirstRow();
23737                 }
23738                 this.fireEvent("afterselectionchange", this);
23739             },
23740             scope: this
23741         });
23742         this.grid.store.on('load', function(){
23743             this.selections.clear();
23744         },this);
23745         /*
23746         var view = this.grid.view;
23747         view.on("refresh", this.onRefresh, this);
23748         view.on("rowupdated", this.onRowUpdated, this);
23749         view.on("rowremoved", this.onRemove, this);
23750         */
23751     },
23752
23753     // private
23754     onRefresh : function()
23755     {
23756         var ds = this.grid.store, i, v = this.grid.view;
23757         var s = this.selections;
23758         s.each(function(r){
23759             if((i = ds.indexOfId(r.id)) != -1){
23760                 v.onRowSelect(i);
23761             }else{
23762                 s.remove(r);
23763             }
23764         });
23765     },
23766
23767     // private
23768     onRemove : function(v, index, r){
23769         this.selections.remove(r);
23770     },
23771
23772     // private
23773     onRowUpdated : function(v, index, r){
23774         if(this.isSelected(r)){
23775             v.onRowSelect(index);
23776         }
23777     },
23778
23779     /**
23780      * Select records.
23781      * @param {Array} records The records to select
23782      * @param {Boolean} keepExisting (optional) True to keep existing selections
23783      */
23784     selectRecords : function(records, keepExisting)
23785     {
23786         if(!keepExisting){
23787             this.clearSelections();
23788         }
23789             var ds = this.grid.store;
23790         for(var i = 0, len = records.length; i < len; i++){
23791             this.selectRow(ds.indexOf(records[i]), true);
23792         }
23793     },
23794
23795     /**
23796      * Gets the number of selected rows.
23797      * @return {Number}
23798      */
23799     getCount : function(){
23800         return this.selections.length;
23801     },
23802
23803     /**
23804      * Selects the first row in the grid.
23805      */
23806     selectFirstRow : function(){
23807         this.selectRow(0);
23808     },
23809
23810     /**
23811      * Select the last row.
23812      * @param {Boolean} keepExisting (optional) True to keep existing selections
23813      */
23814     selectLastRow : function(keepExisting){
23815         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23816         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23817     },
23818
23819     /**
23820      * Selects the row immediately following the last selected row.
23821      * @param {Boolean} keepExisting (optional) True to keep existing selections
23822      */
23823     selectNext : function(keepExisting)
23824     {
23825             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23826             this.selectRow(this.last+1, keepExisting);
23827             this.grid.getView().focusRow(this.last);
23828         }
23829     },
23830
23831     /**
23832      * Selects the row that precedes the last selected row.
23833      * @param {Boolean} keepExisting (optional) True to keep existing selections
23834      */
23835     selectPrevious : function(keepExisting){
23836         if(this.last){
23837             this.selectRow(this.last-1, keepExisting);
23838             this.grid.getView().focusRow(this.last);
23839         }
23840     },
23841
23842     /**
23843      * Returns the selected records
23844      * @return {Array} Array of selected records
23845      */
23846     getSelections : function(){
23847         return [].concat(this.selections.items);
23848     },
23849
23850     /**
23851      * Returns the first selected record.
23852      * @return {Record}
23853      */
23854     getSelected : function(){
23855         return this.selections.itemAt(0);
23856     },
23857
23858
23859     /**
23860      * Clears all selections.
23861      */
23862     clearSelections : function(fast)
23863     {
23864         if(this.locked) {
23865             return;
23866         }
23867         if(fast !== true){
23868                 var ds = this.grid.store;
23869             var s = this.selections;
23870             s.each(function(r){
23871                 this.deselectRow(ds.indexOfId(r.id));
23872             }, this);
23873             s.clear();
23874         }else{
23875             this.selections.clear();
23876         }
23877         this.last = false;
23878     },
23879
23880
23881     /**
23882      * Selects all rows.
23883      */
23884     selectAll : function(){
23885         if(this.locked) {
23886             return;
23887         }
23888         this.selections.clear();
23889         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23890             this.selectRow(i, true);
23891         }
23892     },
23893
23894     /**
23895      * Returns True if there is a selection.
23896      * @return {Boolean}
23897      */
23898     hasSelection : function(){
23899         return this.selections.length > 0;
23900     },
23901
23902     /**
23903      * Returns True if the specified row is selected.
23904      * @param {Number/Record} record The record or index of the record to check
23905      * @return {Boolean}
23906      */
23907     isSelected : function(index){
23908             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23909         return (r && this.selections.key(r.id) ? true : false);
23910     },
23911
23912     /**
23913      * Returns True if the specified record id is selected.
23914      * @param {String} id The id of record to check
23915      * @return {Boolean}
23916      */
23917     isIdSelected : function(id){
23918         return (this.selections.key(id) ? true : false);
23919     },
23920
23921
23922     // private
23923     handleMouseDBClick : function(e, t){
23924         
23925     },
23926     // private
23927     handleMouseDown : function(e, t)
23928     {
23929             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23930         if(this.isLocked() || rowIndex < 0 ){
23931             return;
23932         };
23933         if(e.shiftKey && this.last !== false){
23934             var last = this.last;
23935             this.selectRange(last, rowIndex, e.ctrlKey);
23936             this.last = last; // reset the last
23937             t.focus();
23938     
23939         }else{
23940             var isSelected = this.isSelected(rowIndex);
23941             //Roo.log("select row:" + rowIndex);
23942             if(isSelected){
23943                 this.deselectRow(rowIndex);
23944             } else {
23945                         this.selectRow(rowIndex, true);
23946             }
23947     
23948             /*
23949                 if(e.button !== 0 && isSelected){
23950                 alert('rowIndex 2: ' + rowIndex);
23951                     view.focusRow(rowIndex);
23952                 }else if(e.ctrlKey && isSelected){
23953                     this.deselectRow(rowIndex);
23954                 }else if(!isSelected){
23955                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23956                     view.focusRow(rowIndex);
23957                 }
23958             */
23959         }
23960         this.fireEvent("afterselectionchange", this);
23961     },
23962     // private
23963     handleDragableRowClick :  function(grid, rowIndex, e) 
23964     {
23965         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23966             this.selectRow(rowIndex, false);
23967             grid.view.focusRow(rowIndex);
23968              this.fireEvent("afterselectionchange", this);
23969         }
23970     },
23971     
23972     /**
23973      * Selects multiple rows.
23974      * @param {Array} rows Array of the indexes of the row to select
23975      * @param {Boolean} keepExisting (optional) True to keep existing selections
23976      */
23977     selectRows : function(rows, keepExisting){
23978         if(!keepExisting){
23979             this.clearSelections();
23980         }
23981         for(var i = 0, len = rows.length; i < len; i++){
23982             this.selectRow(rows[i], true);
23983         }
23984     },
23985
23986     /**
23987      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23988      * @param {Number} startRow The index of the first row in the range
23989      * @param {Number} endRow The index of the last row in the range
23990      * @param {Boolean} keepExisting (optional) True to retain existing selections
23991      */
23992     selectRange : function(startRow, endRow, keepExisting){
23993         if(this.locked) {
23994             return;
23995         }
23996         if(!keepExisting){
23997             this.clearSelections();
23998         }
23999         if(startRow <= endRow){
24000             for(var i = startRow; i <= endRow; i++){
24001                 this.selectRow(i, true);
24002             }
24003         }else{
24004             for(var i = startRow; i >= endRow; i--){
24005                 this.selectRow(i, true);
24006             }
24007         }
24008     },
24009
24010     /**
24011      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24012      * @param {Number} startRow The index of the first row in the range
24013      * @param {Number} endRow The index of the last row in the range
24014      */
24015     deselectRange : function(startRow, endRow, preventViewNotify){
24016         if(this.locked) {
24017             return;
24018         }
24019         for(var i = startRow; i <= endRow; i++){
24020             this.deselectRow(i, preventViewNotify);
24021         }
24022     },
24023
24024     /**
24025      * Selects a row.
24026      * @param {Number} row The index of the row to select
24027      * @param {Boolean} keepExisting (optional) True to keep existing selections
24028      */
24029     selectRow : function(index, keepExisting, preventViewNotify)
24030     {
24031             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24032             return;
24033         }
24034         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24035             if(!keepExisting || this.singleSelect){
24036                 this.clearSelections();
24037             }
24038             
24039             var r = this.grid.store.getAt(index);
24040             //console.log('selectRow - record id :' + r.id);
24041             
24042             this.selections.add(r);
24043             this.last = this.lastActive = index;
24044             if(!preventViewNotify){
24045                 var proxy = new Roo.Element(
24046                                 this.grid.getRowDom(index)
24047                 );
24048                 proxy.addClass('bg-info info');
24049             }
24050             this.fireEvent("rowselect", this, index, r);
24051             this.fireEvent("selectionchange", this);
24052         }
24053     },
24054
24055     /**
24056      * Deselects a row.
24057      * @param {Number} row The index of the row to deselect
24058      */
24059     deselectRow : function(index, preventViewNotify)
24060     {
24061         if(this.locked) {
24062             return;
24063         }
24064         if(this.last == index){
24065             this.last = false;
24066         }
24067         if(this.lastActive == index){
24068             this.lastActive = false;
24069         }
24070         
24071         var r = this.grid.store.getAt(index);
24072         if (!r) {
24073             return;
24074         }
24075         
24076         this.selections.remove(r);
24077         //.console.log('deselectRow - record id :' + r.id);
24078         if(!preventViewNotify){
24079         
24080             var proxy = new Roo.Element(
24081                 this.grid.getRowDom(index)
24082             );
24083             proxy.removeClass('bg-info info');
24084         }
24085         this.fireEvent("rowdeselect", this, index);
24086         this.fireEvent("selectionchange", this);
24087     },
24088
24089     // private
24090     restoreLast : function(){
24091         if(this._last){
24092             this.last = this._last;
24093         }
24094     },
24095
24096     // private
24097     acceptsNav : function(row, col, cm){
24098         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24099     },
24100
24101     // private
24102     onEditorKey : function(field, e){
24103         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24104         if(k == e.TAB){
24105             e.stopEvent();
24106             ed.completeEdit();
24107             if(e.shiftKey){
24108                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24109             }else{
24110                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24111             }
24112         }else if(k == e.ENTER && !e.ctrlKey){
24113             e.stopEvent();
24114             ed.completeEdit();
24115             if(e.shiftKey){
24116                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24117             }else{
24118                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24119             }
24120         }else if(k == e.ESC){
24121             ed.cancelEdit();
24122         }
24123         if(newCell){
24124             g.startEditing(newCell[0], newCell[1]);
24125         }
24126     }
24127 });
24128 /*
24129  * Based on:
24130  * Ext JS Library 1.1.1
24131  * Copyright(c) 2006-2007, Ext JS, LLC.
24132  *
24133  * Originally Released Under LGPL - original licence link has changed is not relivant.
24134  *
24135  * Fork - LGPL
24136  * <script type="text/javascript">
24137  */
24138  
24139 /**
24140  * @class Roo.bootstrap.PagingToolbar
24141  * @extends Roo.bootstrap.NavSimplebar
24142  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24143  * @constructor
24144  * Create a new PagingToolbar
24145  * @param {Object} config The config object
24146  * @param {Roo.data.Store} store
24147  */
24148 Roo.bootstrap.PagingToolbar = function(config)
24149 {
24150     // old args format still supported... - xtype is prefered..
24151         // created from xtype...
24152     
24153     this.ds = config.dataSource;
24154     
24155     if (config.store && !this.ds) {
24156         this.store= Roo.factory(config.store, Roo.data);
24157         this.ds = this.store;
24158         this.ds.xmodule = this.xmodule || false;
24159     }
24160     
24161     this.toolbarItems = [];
24162     if (config.items) {
24163         this.toolbarItems = config.items;
24164     }
24165     
24166     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24167     
24168     this.cursor = 0;
24169     
24170     if (this.ds) { 
24171         this.bind(this.ds);
24172     }
24173     
24174     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24175     
24176 };
24177
24178 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24179     /**
24180      * @cfg {Roo.data.Store} dataSource
24181      * The underlying data store providing the paged data
24182      */
24183     /**
24184      * @cfg {String/HTMLElement/Element} container
24185      * container The id or element that will contain the toolbar
24186      */
24187     /**
24188      * @cfg {Boolean} displayInfo
24189      * True to display the displayMsg (defaults to false)
24190      */
24191     /**
24192      * @cfg {Number} pageSize
24193      * The number of records to display per page (defaults to 20)
24194      */
24195     pageSize: 20,
24196     /**
24197      * @cfg {String} displayMsg
24198      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24199      */
24200     displayMsg : 'Displaying {0} - {1} of {2}',
24201     /**
24202      * @cfg {String} emptyMsg
24203      * The message to display when no records are found (defaults to "No data to display")
24204      */
24205     emptyMsg : 'No data to display',
24206     /**
24207      * Customizable piece of the default paging text (defaults to "Page")
24208      * @type String
24209      */
24210     beforePageText : "Page",
24211     /**
24212      * Customizable piece of the default paging text (defaults to "of %0")
24213      * @type String
24214      */
24215     afterPageText : "of {0}",
24216     /**
24217      * Customizable piece of the default paging text (defaults to "First Page")
24218      * @type String
24219      */
24220     firstText : "First Page",
24221     /**
24222      * Customizable piece of the default paging text (defaults to "Previous Page")
24223      * @type String
24224      */
24225     prevText : "Previous Page",
24226     /**
24227      * Customizable piece of the default paging text (defaults to "Next Page")
24228      * @type String
24229      */
24230     nextText : "Next Page",
24231     /**
24232      * Customizable piece of the default paging text (defaults to "Last Page")
24233      * @type String
24234      */
24235     lastText : "Last Page",
24236     /**
24237      * Customizable piece of the default paging text (defaults to "Refresh")
24238      * @type String
24239      */
24240     refreshText : "Refresh",
24241
24242     buttons : false,
24243     // private
24244     onRender : function(ct, position) 
24245     {
24246         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24247         this.navgroup.parentId = this.id;
24248         this.navgroup.onRender(this.el, null);
24249         // add the buttons to the navgroup
24250         
24251         if(this.displayInfo){
24252             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24253             this.displayEl = this.el.select('.x-paging-info', true).first();
24254 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24255 //            this.displayEl = navel.el.select('span',true).first();
24256         }
24257         
24258         var _this = this;
24259         
24260         if(this.buttons){
24261             Roo.each(_this.buttons, function(e){ // this might need to use render????
24262                Roo.factory(e).onRender(_this.el, null);
24263             });
24264         }
24265             
24266         Roo.each(_this.toolbarItems, function(e) {
24267             _this.navgroup.addItem(e);
24268         });
24269         
24270         
24271         this.first = this.navgroup.addItem({
24272             tooltip: this.firstText,
24273             cls: "prev",
24274             icon : 'fa fa-backward',
24275             disabled: true,
24276             preventDefault: true,
24277             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24278         });
24279         
24280         this.prev =  this.navgroup.addItem({
24281             tooltip: this.prevText,
24282             cls: "prev",
24283             icon : 'fa fa-step-backward',
24284             disabled: true,
24285             preventDefault: true,
24286             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24287         });
24288     //this.addSeparator();
24289         
24290         
24291         var field = this.navgroup.addItem( {
24292             tagtype : 'span',
24293             cls : 'x-paging-position',
24294             
24295             html : this.beforePageText  +
24296                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24297                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24298          } ); //?? escaped?
24299         
24300         this.field = field.el.select('input', true).first();
24301         this.field.on("keydown", this.onPagingKeydown, this);
24302         this.field.on("focus", function(){this.dom.select();});
24303     
24304     
24305         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24306         //this.field.setHeight(18);
24307         //this.addSeparator();
24308         this.next = this.navgroup.addItem({
24309             tooltip: this.nextText,
24310             cls: "next",
24311             html : ' <i class="fa fa-step-forward">',
24312             disabled: true,
24313             preventDefault: true,
24314             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24315         });
24316         this.last = this.navgroup.addItem({
24317             tooltip: this.lastText,
24318             icon : 'fa fa-forward',
24319             cls: "next",
24320             disabled: true,
24321             preventDefault: true,
24322             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24323         });
24324     //this.addSeparator();
24325         this.loading = this.navgroup.addItem({
24326             tooltip: this.refreshText,
24327             icon: 'fa fa-refresh',
24328             preventDefault: true,
24329             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24330         });
24331         
24332     },
24333
24334     // private
24335     updateInfo : function(){
24336         if(this.displayEl){
24337             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24338             var msg = count == 0 ?
24339                 this.emptyMsg :
24340                 String.format(
24341                     this.displayMsg,
24342                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24343                 );
24344             this.displayEl.update(msg);
24345         }
24346     },
24347
24348     // private
24349     onLoad : function(ds, r, o)
24350     {
24351         this.cursor = o.params ? o.params.start : 0;
24352         var d = this.getPageData(),
24353             ap = d.activePage,
24354             ps = d.pages;
24355         
24356         
24357         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24358         this.field.dom.value = ap;
24359         this.first.setDisabled(ap == 1);
24360         this.prev.setDisabled(ap == 1);
24361         this.next.setDisabled(ap == ps);
24362         this.last.setDisabled(ap == ps);
24363         this.loading.enable();
24364         this.updateInfo();
24365     },
24366
24367     // private
24368     getPageData : function(){
24369         var total = this.ds.getTotalCount();
24370         return {
24371             total : total,
24372             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24373             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24374         };
24375     },
24376
24377     // private
24378     onLoadError : function(){
24379         this.loading.enable();
24380     },
24381
24382     // private
24383     onPagingKeydown : function(e){
24384         var k = e.getKey();
24385         var d = this.getPageData();
24386         if(k == e.RETURN){
24387             var v = this.field.dom.value, pageNum;
24388             if(!v || isNaN(pageNum = parseInt(v, 10))){
24389                 this.field.dom.value = d.activePage;
24390                 return;
24391             }
24392             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24393             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24394             e.stopEvent();
24395         }
24396         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))
24397         {
24398           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24399           this.field.dom.value = pageNum;
24400           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24401           e.stopEvent();
24402         }
24403         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24404         {
24405           var v = this.field.dom.value, pageNum; 
24406           var increment = (e.shiftKey) ? 10 : 1;
24407           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24408                 increment *= -1;
24409           }
24410           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24411             this.field.dom.value = d.activePage;
24412             return;
24413           }
24414           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24415           {
24416             this.field.dom.value = parseInt(v, 10) + increment;
24417             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24418             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24419           }
24420           e.stopEvent();
24421         }
24422     },
24423
24424     // private
24425     beforeLoad : function(){
24426         if(this.loading){
24427             this.loading.disable();
24428         }
24429     },
24430
24431     // private
24432     onClick : function(which){
24433         
24434         var ds = this.ds;
24435         if (!ds) {
24436             return;
24437         }
24438         
24439         switch(which){
24440             case "first":
24441                 ds.load({params:{start: 0, limit: this.pageSize}});
24442             break;
24443             case "prev":
24444                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24445             break;
24446             case "next":
24447                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24448             break;
24449             case "last":
24450                 var total = ds.getTotalCount();
24451                 var extra = total % this.pageSize;
24452                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24453                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24454             break;
24455             case "refresh":
24456                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24457             break;
24458         }
24459     },
24460
24461     /**
24462      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24463      * @param {Roo.data.Store} store The data store to unbind
24464      */
24465     unbind : function(ds){
24466         ds.un("beforeload", this.beforeLoad, this);
24467         ds.un("load", this.onLoad, this);
24468         ds.un("loadexception", this.onLoadError, this);
24469         ds.un("remove", this.updateInfo, this);
24470         ds.un("add", this.updateInfo, this);
24471         this.ds = undefined;
24472     },
24473
24474     /**
24475      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24476      * @param {Roo.data.Store} store The data store to bind
24477      */
24478     bind : function(ds){
24479         ds.on("beforeload", this.beforeLoad, this);
24480         ds.on("load", this.onLoad, this);
24481         ds.on("loadexception", this.onLoadError, this);
24482         ds.on("remove", this.updateInfo, this);
24483         ds.on("add", this.updateInfo, this);
24484         this.ds = ds;
24485     }
24486 });/*
24487  * - LGPL
24488  *
24489  * element
24490  * 
24491  */
24492
24493 /**
24494  * @class Roo.bootstrap.MessageBar
24495  * @extends Roo.bootstrap.Component
24496  * Bootstrap MessageBar class
24497  * @cfg {String} html contents of the MessageBar
24498  * @cfg {String} weight (info | success | warning | danger) default info
24499  * @cfg {String} beforeClass insert the bar before the given class
24500  * @cfg {Boolean} closable (true | false) default false
24501  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24502  * 
24503  * @constructor
24504  * Create a new Element
24505  * @param {Object} config The config object
24506  */
24507
24508 Roo.bootstrap.MessageBar = function(config){
24509     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24510 };
24511
24512 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24513     
24514     html: '',
24515     weight: 'info',
24516     closable: false,
24517     fixed: false,
24518     beforeClass: 'bootstrap-sticky-wrap',
24519     
24520     getAutoCreate : function(){
24521         
24522         var cfg = {
24523             tag: 'div',
24524             cls: 'alert alert-dismissable alert-' + this.weight,
24525             cn: [
24526                 {
24527                     tag: 'span',
24528                     cls: 'message',
24529                     html: this.html || ''
24530                 }
24531             ]
24532         };
24533         
24534         if(this.fixed){
24535             cfg.cls += ' alert-messages-fixed';
24536         }
24537         
24538         if(this.closable){
24539             cfg.cn.push({
24540                 tag: 'button',
24541                 cls: 'close',
24542                 html: 'x'
24543             });
24544         }
24545         
24546         return cfg;
24547     },
24548     
24549     onRender : function(ct, position)
24550     {
24551         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24552         
24553         if(!this.el){
24554             var cfg = Roo.apply({},  this.getAutoCreate());
24555             cfg.id = Roo.id();
24556             
24557             if (this.cls) {
24558                 cfg.cls += ' ' + this.cls;
24559             }
24560             if (this.style) {
24561                 cfg.style = this.style;
24562             }
24563             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24564             
24565             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24566         }
24567         
24568         this.el.select('>button.close').on('click', this.hide, this);
24569         
24570     },
24571     
24572     show : function()
24573     {
24574         if (!this.rendered) {
24575             this.render();
24576         }
24577         
24578         this.el.show();
24579         
24580         this.fireEvent('show', this);
24581         
24582     },
24583     
24584     hide : function()
24585     {
24586         if (!this.rendered) {
24587             this.render();
24588         }
24589         
24590         this.el.hide();
24591         
24592         this.fireEvent('hide', this);
24593     },
24594     
24595     update : function()
24596     {
24597 //        var e = this.el.dom.firstChild;
24598 //        
24599 //        if(this.closable){
24600 //            e = e.nextSibling;
24601 //        }
24602 //        
24603 //        e.data = this.html || '';
24604
24605         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24606     }
24607    
24608 });
24609
24610  
24611
24612      /*
24613  * - LGPL
24614  *
24615  * Graph
24616  * 
24617  */
24618
24619
24620 /**
24621  * @class Roo.bootstrap.Graph
24622  * @extends Roo.bootstrap.Component
24623  * Bootstrap Graph class
24624 > Prameters
24625  -sm {number} sm 4
24626  -md {number} md 5
24627  @cfg {String} graphtype  bar | vbar | pie
24628  @cfg {number} g_x coodinator | centre x (pie)
24629  @cfg {number} g_y coodinator | centre y (pie)
24630  @cfg {number} g_r radius (pie)
24631  @cfg {number} g_height height of the chart (respected by all elements in the set)
24632  @cfg {number} g_width width of the chart (respected by all elements in the set)
24633  @cfg {Object} title The title of the chart
24634     
24635  -{Array}  values
24636  -opts (object) options for the chart 
24637      o {
24638      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24639      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24640      o vgutter (number)
24641      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.
24642      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24643      o to
24644      o stretch (boolean)
24645      o }
24646  -opts (object) options for the pie
24647      o{
24648      o cut
24649      o startAngle (number)
24650      o endAngle (number)
24651      } 
24652  *
24653  * @constructor
24654  * Create a new Input
24655  * @param {Object} config The config object
24656  */
24657
24658 Roo.bootstrap.Graph = function(config){
24659     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24660     
24661     this.addEvents({
24662         // img events
24663         /**
24664          * @event click
24665          * The img click event for the img.
24666          * @param {Roo.EventObject} e
24667          */
24668         "click" : true
24669     });
24670 };
24671
24672 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24673     
24674     sm: 4,
24675     md: 5,
24676     graphtype: 'bar',
24677     g_height: 250,
24678     g_width: 400,
24679     g_x: 50,
24680     g_y: 50,
24681     g_r: 30,
24682     opts:{
24683         //g_colors: this.colors,
24684         g_type: 'soft',
24685         g_gutter: '20%'
24686
24687     },
24688     title : false,
24689
24690     getAutoCreate : function(){
24691         
24692         var cfg = {
24693             tag: 'div',
24694             html : null
24695         };
24696         
24697         
24698         return  cfg;
24699     },
24700
24701     onRender : function(ct,position){
24702         
24703         
24704         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24705         
24706         if (typeof(Raphael) == 'undefined') {
24707             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24708             return;
24709         }
24710         
24711         this.raphael = Raphael(this.el.dom);
24712         
24713                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24714                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24715                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24716                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24717                 /*
24718                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24719                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24720                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24721                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24722                 
24723                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24724                 r.barchart(330, 10, 300, 220, data1);
24725                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24726                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24727                 */
24728                 
24729                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24730                 // r.barchart(30, 30, 560, 250,  xdata, {
24731                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24732                 //     axis : "0 0 1 1",
24733                 //     axisxlabels :  xdata
24734                 //     //yvalues : cols,
24735                    
24736                 // });
24737 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24738 //        
24739 //        this.load(null,xdata,{
24740 //                axis : "0 0 1 1",
24741 //                axisxlabels :  xdata
24742 //                });
24743
24744     },
24745
24746     load : function(graphtype,xdata,opts)
24747     {
24748         this.raphael.clear();
24749         if(!graphtype) {
24750             graphtype = this.graphtype;
24751         }
24752         if(!opts){
24753             opts = this.opts;
24754         }
24755         var r = this.raphael,
24756             fin = function () {
24757                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24758             },
24759             fout = function () {
24760                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24761             },
24762             pfin = function() {
24763                 this.sector.stop();
24764                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24765
24766                 if (this.label) {
24767                     this.label[0].stop();
24768                     this.label[0].attr({ r: 7.5 });
24769                     this.label[1].attr({ "font-weight": 800 });
24770                 }
24771             },
24772             pfout = function() {
24773                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24774
24775                 if (this.label) {
24776                     this.label[0].animate({ r: 5 }, 500, "bounce");
24777                     this.label[1].attr({ "font-weight": 400 });
24778                 }
24779             };
24780
24781         switch(graphtype){
24782             case 'bar':
24783                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24784                 break;
24785             case 'hbar':
24786                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24787                 break;
24788             case 'pie':
24789 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24790 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24791 //            
24792                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24793                 
24794                 break;
24795
24796         }
24797         
24798         if(this.title){
24799             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24800         }
24801         
24802     },
24803     
24804     setTitle: function(o)
24805     {
24806         this.title = o;
24807     },
24808     
24809     initEvents: function() {
24810         
24811         if(!this.href){
24812             this.el.on('click', this.onClick, this);
24813         }
24814     },
24815     
24816     onClick : function(e)
24817     {
24818         Roo.log('img onclick');
24819         this.fireEvent('click', this, e);
24820     }
24821    
24822 });
24823
24824  
24825 /*
24826  * - LGPL
24827  *
24828  * numberBox
24829  * 
24830  */
24831 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24832
24833 /**
24834  * @class Roo.bootstrap.dash.NumberBox
24835  * @extends Roo.bootstrap.Component
24836  * Bootstrap NumberBox class
24837  * @cfg {String} headline Box headline
24838  * @cfg {String} content Box content
24839  * @cfg {String} icon Box icon
24840  * @cfg {String} footer Footer text
24841  * @cfg {String} fhref Footer href
24842  * 
24843  * @constructor
24844  * Create a new NumberBox
24845  * @param {Object} config The config object
24846  */
24847
24848
24849 Roo.bootstrap.dash.NumberBox = function(config){
24850     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24851     
24852 };
24853
24854 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24855     
24856     headline : '',
24857     content : '',
24858     icon : '',
24859     footer : '',
24860     fhref : '',
24861     ficon : '',
24862     
24863     getAutoCreate : function(){
24864         
24865         var cfg = {
24866             tag : 'div',
24867             cls : 'small-box ',
24868             cn : [
24869                 {
24870                     tag : 'div',
24871                     cls : 'inner',
24872                     cn :[
24873                         {
24874                             tag : 'h3',
24875                             cls : 'roo-headline',
24876                             html : this.headline
24877                         },
24878                         {
24879                             tag : 'p',
24880                             cls : 'roo-content',
24881                             html : this.content
24882                         }
24883                     ]
24884                 }
24885             ]
24886         };
24887         
24888         if(this.icon){
24889             cfg.cn.push({
24890                 tag : 'div',
24891                 cls : 'icon',
24892                 cn :[
24893                     {
24894                         tag : 'i',
24895                         cls : 'ion ' + this.icon
24896                     }
24897                 ]
24898             });
24899         }
24900         
24901         if(this.footer){
24902             var footer = {
24903                 tag : 'a',
24904                 cls : 'small-box-footer',
24905                 href : this.fhref || '#',
24906                 html : this.footer
24907             };
24908             
24909             cfg.cn.push(footer);
24910             
24911         }
24912         
24913         return  cfg;
24914     },
24915
24916     onRender : function(ct,position){
24917         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24918
24919
24920        
24921                 
24922     },
24923
24924     setHeadline: function (value)
24925     {
24926         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24927     },
24928     
24929     setFooter: function (value, href)
24930     {
24931         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24932         
24933         if(href){
24934             this.el.select('a.small-box-footer',true).first().attr('href', href);
24935         }
24936         
24937     },
24938
24939     setContent: function (value)
24940     {
24941         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24942     },
24943
24944     initEvents: function() 
24945     {   
24946         
24947     }
24948     
24949 });
24950
24951  
24952 /*
24953  * - LGPL
24954  *
24955  * TabBox
24956  * 
24957  */
24958 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24959
24960 /**
24961  * @class Roo.bootstrap.dash.TabBox
24962  * @extends Roo.bootstrap.Component
24963  * Bootstrap TabBox class
24964  * @cfg {String} title Title of the TabBox
24965  * @cfg {String} icon Icon of the TabBox
24966  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24967  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24968  * 
24969  * @constructor
24970  * Create a new TabBox
24971  * @param {Object} config The config object
24972  */
24973
24974
24975 Roo.bootstrap.dash.TabBox = function(config){
24976     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24977     this.addEvents({
24978         // raw events
24979         /**
24980          * @event addpane
24981          * When a pane is added
24982          * @param {Roo.bootstrap.dash.TabPane} pane
24983          */
24984         "addpane" : true,
24985         /**
24986          * @event activatepane
24987          * When a pane is activated
24988          * @param {Roo.bootstrap.dash.TabPane} pane
24989          */
24990         "activatepane" : true
24991         
24992          
24993     });
24994     
24995     this.panes = [];
24996 };
24997
24998 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24999
25000     title : '',
25001     icon : false,
25002     showtabs : true,
25003     tabScrollable : false,
25004     
25005     getChildContainer : function()
25006     {
25007         return this.el.select('.tab-content', true).first();
25008     },
25009     
25010     getAutoCreate : function(){
25011         
25012         var header = {
25013             tag: 'li',
25014             cls: 'pull-left header',
25015             html: this.title,
25016             cn : []
25017         };
25018         
25019         if(this.icon){
25020             header.cn.push({
25021                 tag: 'i',
25022                 cls: 'fa ' + this.icon
25023             });
25024         }
25025         
25026         var h = {
25027             tag: 'ul',
25028             cls: 'nav nav-tabs pull-right',
25029             cn: [
25030                 header
25031             ]
25032         };
25033         
25034         if(this.tabScrollable){
25035             h = {
25036                 tag: 'div',
25037                 cls: 'tab-header',
25038                 cn: [
25039                     {
25040                         tag: 'ul',
25041                         cls: 'nav nav-tabs pull-right',
25042                         cn: [
25043                             header
25044                         ]
25045                     }
25046                 ]
25047             };
25048         }
25049         
25050         var cfg = {
25051             tag: 'div',
25052             cls: 'nav-tabs-custom',
25053             cn: [
25054                 h,
25055                 {
25056                     tag: 'div',
25057                     cls: 'tab-content no-padding',
25058                     cn: []
25059                 }
25060             ]
25061         };
25062
25063         return  cfg;
25064     },
25065     initEvents : function()
25066     {
25067         //Roo.log('add add pane handler');
25068         this.on('addpane', this.onAddPane, this);
25069     },
25070      /**
25071      * Updates the box title
25072      * @param {String} html to set the title to.
25073      */
25074     setTitle : function(value)
25075     {
25076         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25077     },
25078     onAddPane : function(pane)
25079     {
25080         this.panes.push(pane);
25081         //Roo.log('addpane');
25082         //Roo.log(pane);
25083         // tabs are rendere left to right..
25084         if(!this.showtabs){
25085             return;
25086         }
25087         
25088         var ctr = this.el.select('.nav-tabs', true).first();
25089          
25090          
25091         var existing = ctr.select('.nav-tab',true);
25092         var qty = existing.getCount();;
25093         
25094         
25095         var tab = ctr.createChild({
25096             tag : 'li',
25097             cls : 'nav-tab' + (qty ? '' : ' active'),
25098             cn : [
25099                 {
25100                     tag : 'a',
25101                     href:'#',
25102                     html : pane.title
25103                 }
25104             ]
25105         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25106         pane.tab = tab;
25107         
25108         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25109         if (!qty) {
25110             pane.el.addClass('active');
25111         }
25112         
25113                 
25114     },
25115     onTabClick : function(ev,un,ob,pane)
25116     {
25117         //Roo.log('tab - prev default');
25118         ev.preventDefault();
25119         
25120         
25121         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25122         pane.tab.addClass('active');
25123         //Roo.log(pane.title);
25124         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25125         // technically we should have a deactivate event.. but maybe add later.
25126         // and it should not de-activate the selected tab...
25127         this.fireEvent('activatepane', pane);
25128         pane.el.addClass('active');
25129         pane.fireEvent('activate');
25130         
25131         
25132     },
25133     
25134     getActivePane : function()
25135     {
25136         var r = false;
25137         Roo.each(this.panes, function(p) {
25138             if(p.el.hasClass('active')){
25139                 r = p;
25140                 return false;
25141             }
25142             
25143             return;
25144         });
25145         
25146         return r;
25147     }
25148     
25149     
25150 });
25151
25152  
25153 /*
25154  * - LGPL
25155  *
25156  * Tab pane
25157  * 
25158  */
25159 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25160 /**
25161  * @class Roo.bootstrap.TabPane
25162  * @extends Roo.bootstrap.Component
25163  * Bootstrap TabPane class
25164  * @cfg {Boolean} active (false | true) Default false
25165  * @cfg {String} title title of panel
25166
25167  * 
25168  * @constructor
25169  * Create a new TabPane
25170  * @param {Object} config The config object
25171  */
25172
25173 Roo.bootstrap.dash.TabPane = function(config){
25174     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25175     
25176     this.addEvents({
25177         // raw events
25178         /**
25179          * @event activate
25180          * When a pane is activated
25181          * @param {Roo.bootstrap.dash.TabPane} pane
25182          */
25183         "activate" : true
25184          
25185     });
25186 };
25187
25188 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25189     
25190     active : false,
25191     title : '',
25192     
25193     // the tabBox that this is attached to.
25194     tab : false,
25195      
25196     getAutoCreate : function() 
25197     {
25198         var cfg = {
25199             tag: 'div',
25200             cls: 'tab-pane'
25201         };
25202         
25203         if(this.active){
25204             cfg.cls += ' active';
25205         }
25206         
25207         return cfg;
25208     },
25209     initEvents  : function()
25210     {
25211         //Roo.log('trigger add pane handler');
25212         this.parent().fireEvent('addpane', this)
25213     },
25214     
25215      /**
25216      * Updates the tab title 
25217      * @param {String} html to set the title to.
25218      */
25219     setTitle: function(str)
25220     {
25221         if (!this.tab) {
25222             return;
25223         }
25224         this.title = str;
25225         this.tab.select('a', true).first().dom.innerHTML = str;
25226         
25227     }
25228     
25229     
25230     
25231 });
25232
25233  
25234
25235
25236  /*
25237  * - LGPL
25238  *
25239  * menu
25240  * 
25241  */
25242 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25243
25244 /**
25245  * @class Roo.bootstrap.menu.Menu
25246  * @extends Roo.bootstrap.Component
25247  * Bootstrap Menu class - container for Menu
25248  * @cfg {String} html Text of the menu
25249  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25250  * @cfg {String} icon Font awesome icon
25251  * @cfg {String} pos Menu align to (top | bottom) default bottom
25252  * 
25253  * 
25254  * @constructor
25255  * Create a new Menu
25256  * @param {Object} config The config object
25257  */
25258
25259
25260 Roo.bootstrap.menu.Menu = function(config){
25261     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25262     
25263     this.addEvents({
25264         /**
25265          * @event beforeshow
25266          * Fires before this menu is displayed
25267          * @param {Roo.bootstrap.menu.Menu} this
25268          */
25269         beforeshow : true,
25270         /**
25271          * @event beforehide
25272          * Fires before this menu is hidden
25273          * @param {Roo.bootstrap.menu.Menu} this
25274          */
25275         beforehide : true,
25276         /**
25277          * @event show
25278          * Fires after this menu is displayed
25279          * @param {Roo.bootstrap.menu.Menu} this
25280          */
25281         show : true,
25282         /**
25283          * @event hide
25284          * Fires after this menu is hidden
25285          * @param {Roo.bootstrap.menu.Menu} this
25286          */
25287         hide : true,
25288         /**
25289          * @event click
25290          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25291          * @param {Roo.bootstrap.menu.Menu} this
25292          * @param {Roo.EventObject} e
25293          */
25294         click : true
25295     });
25296     
25297 };
25298
25299 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25300     
25301     submenu : false,
25302     html : '',
25303     weight : 'default',
25304     icon : false,
25305     pos : 'bottom',
25306     
25307     
25308     getChildContainer : function() {
25309         if(this.isSubMenu){
25310             return this.el;
25311         }
25312         
25313         return this.el.select('ul.dropdown-menu', true).first();  
25314     },
25315     
25316     getAutoCreate : function()
25317     {
25318         var text = [
25319             {
25320                 tag : 'span',
25321                 cls : 'roo-menu-text',
25322                 html : this.html
25323             }
25324         ];
25325         
25326         if(this.icon){
25327             text.unshift({
25328                 tag : 'i',
25329                 cls : 'fa ' + this.icon
25330             })
25331         }
25332         
25333         
25334         var cfg = {
25335             tag : 'div',
25336             cls : 'btn-group',
25337             cn : [
25338                 {
25339                     tag : 'button',
25340                     cls : 'dropdown-button btn btn-' + this.weight,
25341                     cn : text
25342                 },
25343                 {
25344                     tag : 'button',
25345                     cls : 'dropdown-toggle btn btn-' + this.weight,
25346                     cn : [
25347                         {
25348                             tag : 'span',
25349                             cls : 'caret'
25350                         }
25351                     ]
25352                 },
25353                 {
25354                     tag : 'ul',
25355                     cls : 'dropdown-menu'
25356                 }
25357             ]
25358             
25359         };
25360         
25361         if(this.pos == 'top'){
25362             cfg.cls += ' dropup';
25363         }
25364         
25365         if(this.isSubMenu){
25366             cfg = {
25367                 tag : 'ul',
25368                 cls : 'dropdown-menu'
25369             }
25370         }
25371         
25372         return cfg;
25373     },
25374     
25375     onRender : function(ct, position)
25376     {
25377         this.isSubMenu = ct.hasClass('dropdown-submenu');
25378         
25379         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25380     },
25381     
25382     initEvents : function() 
25383     {
25384         if(this.isSubMenu){
25385             return;
25386         }
25387         
25388         this.hidden = true;
25389         
25390         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25391         this.triggerEl.on('click', this.onTriggerPress, this);
25392         
25393         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25394         this.buttonEl.on('click', this.onClick, this);
25395         
25396     },
25397     
25398     list : function()
25399     {
25400         if(this.isSubMenu){
25401             return this.el;
25402         }
25403         
25404         return this.el.select('ul.dropdown-menu', true).first();
25405     },
25406     
25407     onClick : function(e)
25408     {
25409         this.fireEvent("click", this, e);
25410     },
25411     
25412     onTriggerPress  : function(e)
25413     {   
25414         if (this.isVisible()) {
25415             this.hide();
25416         } else {
25417             this.show();
25418         }
25419     },
25420     
25421     isVisible : function(){
25422         return !this.hidden;
25423     },
25424     
25425     show : function()
25426     {
25427         this.fireEvent("beforeshow", this);
25428         
25429         this.hidden = false;
25430         this.el.addClass('open');
25431         
25432         Roo.get(document).on("mouseup", this.onMouseUp, this);
25433         
25434         this.fireEvent("show", this);
25435         
25436         
25437     },
25438     
25439     hide : function()
25440     {
25441         this.fireEvent("beforehide", this);
25442         
25443         this.hidden = true;
25444         this.el.removeClass('open');
25445         
25446         Roo.get(document).un("mouseup", this.onMouseUp);
25447         
25448         this.fireEvent("hide", this);
25449     },
25450     
25451     onMouseUp : function()
25452     {
25453         this.hide();
25454     }
25455     
25456 });
25457
25458  
25459  /*
25460  * - LGPL
25461  *
25462  * menu item
25463  * 
25464  */
25465 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25466
25467 /**
25468  * @class Roo.bootstrap.menu.Item
25469  * @extends Roo.bootstrap.Component
25470  * Bootstrap MenuItem class
25471  * @cfg {Boolean} submenu (true | false) default false
25472  * @cfg {String} html text of the item
25473  * @cfg {String} href the link
25474  * @cfg {Boolean} disable (true | false) default false
25475  * @cfg {Boolean} preventDefault (true | false) default true
25476  * @cfg {String} icon Font awesome icon
25477  * @cfg {String} pos Submenu align to (left | right) default right 
25478  * 
25479  * 
25480  * @constructor
25481  * Create a new Item
25482  * @param {Object} config The config object
25483  */
25484
25485
25486 Roo.bootstrap.menu.Item = function(config){
25487     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25488     this.addEvents({
25489         /**
25490          * @event mouseover
25491          * Fires when the mouse is hovering over this menu
25492          * @param {Roo.bootstrap.menu.Item} this
25493          * @param {Roo.EventObject} e
25494          */
25495         mouseover : true,
25496         /**
25497          * @event mouseout
25498          * Fires when the mouse exits this menu
25499          * @param {Roo.bootstrap.menu.Item} this
25500          * @param {Roo.EventObject} e
25501          */
25502         mouseout : true,
25503         // raw events
25504         /**
25505          * @event click
25506          * The raw click event for the entire grid.
25507          * @param {Roo.EventObject} e
25508          */
25509         click : true
25510     });
25511 };
25512
25513 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25514     
25515     submenu : false,
25516     href : '',
25517     html : '',
25518     preventDefault: true,
25519     disable : false,
25520     icon : false,
25521     pos : 'right',
25522     
25523     getAutoCreate : function()
25524     {
25525         var text = [
25526             {
25527                 tag : 'span',
25528                 cls : 'roo-menu-item-text',
25529                 html : this.html
25530             }
25531         ];
25532         
25533         if(this.icon){
25534             text.unshift({
25535                 tag : 'i',
25536                 cls : 'fa ' + this.icon
25537             })
25538         }
25539         
25540         var cfg = {
25541             tag : 'li',
25542             cn : [
25543                 {
25544                     tag : 'a',
25545                     href : this.href || '#',
25546                     cn : text
25547                 }
25548             ]
25549         };
25550         
25551         if(this.disable){
25552             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25553         }
25554         
25555         if(this.submenu){
25556             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25557             
25558             if(this.pos == 'left'){
25559                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25560             }
25561         }
25562         
25563         return cfg;
25564     },
25565     
25566     initEvents : function() 
25567     {
25568         this.el.on('mouseover', this.onMouseOver, this);
25569         this.el.on('mouseout', this.onMouseOut, this);
25570         
25571         this.el.select('a', true).first().on('click', this.onClick, this);
25572         
25573     },
25574     
25575     onClick : function(e)
25576     {
25577         if(this.preventDefault){
25578             e.preventDefault();
25579         }
25580         
25581         this.fireEvent("click", this, e);
25582     },
25583     
25584     onMouseOver : function(e)
25585     {
25586         if(this.submenu && this.pos == 'left'){
25587             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25588         }
25589         
25590         this.fireEvent("mouseover", this, e);
25591     },
25592     
25593     onMouseOut : function(e)
25594     {
25595         this.fireEvent("mouseout", this, e);
25596     }
25597 });
25598
25599  
25600
25601  /*
25602  * - LGPL
25603  *
25604  * menu separator
25605  * 
25606  */
25607 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25608
25609 /**
25610  * @class Roo.bootstrap.menu.Separator
25611  * @extends Roo.bootstrap.Component
25612  * Bootstrap Separator class
25613  * 
25614  * @constructor
25615  * Create a new Separator
25616  * @param {Object} config The config object
25617  */
25618
25619
25620 Roo.bootstrap.menu.Separator = function(config){
25621     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25622 };
25623
25624 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25625     
25626     getAutoCreate : function(){
25627         var cfg = {
25628             tag : 'li',
25629             cls: 'divider'
25630         };
25631         
25632         return cfg;
25633     }
25634    
25635 });
25636
25637  
25638
25639  /*
25640  * - LGPL
25641  *
25642  * Tooltip
25643  * 
25644  */
25645
25646 /**
25647  * @class Roo.bootstrap.Tooltip
25648  * Bootstrap Tooltip class
25649  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25650  * to determine which dom element triggers the tooltip.
25651  * 
25652  * It needs to add support for additional attributes like tooltip-position
25653  * 
25654  * @constructor
25655  * Create a new Toolti
25656  * @param {Object} config The config object
25657  */
25658
25659 Roo.bootstrap.Tooltip = function(config){
25660     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25661     
25662     this.alignment = Roo.bootstrap.Tooltip.alignment;
25663     
25664     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25665         this.alignment = config.alignment;
25666     }
25667     
25668 };
25669
25670 Roo.apply(Roo.bootstrap.Tooltip, {
25671     /**
25672      * @function init initialize tooltip monitoring.
25673      * @static
25674      */
25675     currentEl : false,
25676     currentTip : false,
25677     currentRegion : false,
25678     
25679     //  init : delay?
25680     
25681     init : function()
25682     {
25683         Roo.get(document).on('mouseover', this.enter ,this);
25684         Roo.get(document).on('mouseout', this.leave, this);
25685          
25686         
25687         this.currentTip = new Roo.bootstrap.Tooltip();
25688     },
25689     
25690     enter : function(ev)
25691     {
25692         var dom = ev.getTarget();
25693         
25694         //Roo.log(['enter',dom]);
25695         var el = Roo.fly(dom);
25696         if (this.currentEl) {
25697             //Roo.log(dom);
25698             //Roo.log(this.currentEl);
25699             //Roo.log(this.currentEl.contains(dom));
25700             if (this.currentEl == el) {
25701                 return;
25702             }
25703             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25704                 return;
25705             }
25706
25707         }
25708         
25709         if (this.currentTip.el) {
25710             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25711         }    
25712         //Roo.log(ev);
25713         
25714         if(!el || el.dom == document){
25715             return;
25716         }
25717         
25718         var bindEl = el;
25719         
25720         // you can not look for children, as if el is the body.. then everythign is the child..
25721         if (!el.attr('tooltip')) { //
25722             if (!el.select("[tooltip]").elements.length) {
25723                 return;
25724             }
25725             // is the mouse over this child...?
25726             bindEl = el.select("[tooltip]").first();
25727             var xy = ev.getXY();
25728             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25729                 //Roo.log("not in region.");
25730                 return;
25731             }
25732             //Roo.log("child element over..");
25733             
25734         }
25735         this.currentEl = bindEl;
25736         this.currentTip.bind(bindEl);
25737         this.currentRegion = Roo.lib.Region.getRegion(dom);
25738         this.currentTip.enter();
25739         
25740     },
25741     leave : function(ev)
25742     {
25743         var dom = ev.getTarget();
25744         //Roo.log(['leave',dom]);
25745         if (!this.currentEl) {
25746             return;
25747         }
25748         
25749         
25750         if (dom != this.currentEl.dom) {
25751             return;
25752         }
25753         var xy = ev.getXY();
25754         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25755             return;
25756         }
25757         // only activate leave if mouse cursor is outside... bounding box..
25758         
25759         
25760         
25761         
25762         if (this.currentTip) {
25763             this.currentTip.leave();
25764         }
25765         //Roo.log('clear currentEl');
25766         this.currentEl = false;
25767         
25768         
25769     },
25770     alignment : {
25771         'left' : ['r-l', [-2,0], 'right'],
25772         'right' : ['l-r', [2,0], 'left'],
25773         'bottom' : ['t-b', [0,2], 'top'],
25774         'top' : [ 'b-t', [0,-2], 'bottom']
25775     }
25776     
25777 });
25778
25779
25780 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25781     
25782     
25783     bindEl : false,
25784     
25785     delay : null, // can be { show : 300 , hide: 500}
25786     
25787     timeout : null,
25788     
25789     hoverState : null, //???
25790     
25791     placement : 'bottom', 
25792     
25793     alignment : false,
25794     
25795     getAutoCreate : function(){
25796     
25797         var cfg = {
25798            cls : 'tooltip',
25799            role : 'tooltip',
25800            cn : [
25801                 {
25802                     cls : 'tooltip-arrow'
25803                 },
25804                 {
25805                     cls : 'tooltip-inner'
25806                 }
25807            ]
25808         };
25809         
25810         return cfg;
25811     },
25812     bind : function(el)
25813     {
25814         this.bindEl = el;
25815     },
25816       
25817     
25818     enter : function () {
25819        
25820         if (this.timeout != null) {
25821             clearTimeout(this.timeout);
25822         }
25823         
25824         this.hoverState = 'in';
25825          //Roo.log("enter - show");
25826         if (!this.delay || !this.delay.show) {
25827             this.show();
25828             return;
25829         }
25830         var _t = this;
25831         this.timeout = setTimeout(function () {
25832             if (_t.hoverState == 'in') {
25833                 _t.show();
25834             }
25835         }, this.delay.show);
25836     },
25837     leave : function()
25838     {
25839         clearTimeout(this.timeout);
25840     
25841         this.hoverState = 'out';
25842          if (!this.delay || !this.delay.hide) {
25843             this.hide();
25844             return;
25845         }
25846        
25847         var _t = this;
25848         this.timeout = setTimeout(function () {
25849             //Roo.log("leave - timeout");
25850             
25851             if (_t.hoverState == 'out') {
25852                 _t.hide();
25853                 Roo.bootstrap.Tooltip.currentEl = false;
25854             }
25855         }, delay);
25856     },
25857     
25858     show : function (msg)
25859     {
25860         if (!this.el) {
25861             this.render(document.body);
25862         }
25863         // set content.
25864         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25865         
25866         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25867         
25868         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25869         
25870         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25871         
25872         var placement = typeof this.placement == 'function' ?
25873             this.placement.call(this, this.el, on_el) :
25874             this.placement;
25875             
25876         var autoToken = /\s?auto?\s?/i;
25877         var autoPlace = autoToken.test(placement);
25878         if (autoPlace) {
25879             placement = placement.replace(autoToken, '') || 'top';
25880         }
25881         
25882         //this.el.detach()
25883         //this.el.setXY([0,0]);
25884         this.el.show();
25885         //this.el.dom.style.display='block';
25886         
25887         //this.el.appendTo(on_el);
25888         
25889         var p = this.getPosition();
25890         var box = this.el.getBox();
25891         
25892         if (autoPlace) {
25893             // fixme..
25894         }
25895         
25896         var align = this.alignment[placement];
25897         
25898         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25899         
25900         if(placement == 'top' || placement == 'bottom'){
25901             if(xy[0] < 0){
25902                 placement = 'right';
25903             }
25904             
25905             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25906                 placement = 'left';
25907             }
25908             
25909             var scroll = Roo.select('body', true).first().getScroll();
25910             
25911             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25912                 placement = 'top';
25913             }
25914             
25915         }
25916         
25917         this.el.alignTo(this.bindEl, align[0],align[1]);
25918         //var arrow = this.el.select('.arrow',true).first();
25919         //arrow.set(align[2], 
25920         
25921         this.el.addClass(placement);
25922         
25923         this.el.addClass('in fade');
25924         
25925         this.hoverState = null;
25926         
25927         if (this.el.hasClass('fade')) {
25928             // fade it?
25929         }
25930         
25931     },
25932     hide : function()
25933     {
25934          
25935         if (!this.el) {
25936             return;
25937         }
25938         //this.el.setXY([0,0]);
25939         this.el.removeClass('in');
25940         //this.el.hide();
25941         
25942     }
25943     
25944 });
25945  
25946
25947  /*
25948  * - LGPL
25949  *
25950  * Location Picker
25951  * 
25952  */
25953
25954 /**
25955  * @class Roo.bootstrap.LocationPicker
25956  * @extends Roo.bootstrap.Component
25957  * Bootstrap LocationPicker class
25958  * @cfg {Number} latitude Position when init default 0
25959  * @cfg {Number} longitude Position when init default 0
25960  * @cfg {Number} zoom default 15
25961  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25962  * @cfg {Boolean} mapTypeControl default false
25963  * @cfg {Boolean} disableDoubleClickZoom default false
25964  * @cfg {Boolean} scrollwheel default true
25965  * @cfg {Boolean} streetViewControl default false
25966  * @cfg {Number} radius default 0
25967  * @cfg {String} locationName
25968  * @cfg {Boolean} draggable default true
25969  * @cfg {Boolean} enableAutocomplete default false
25970  * @cfg {Boolean} enableReverseGeocode default true
25971  * @cfg {String} markerTitle
25972  * 
25973  * @constructor
25974  * Create a new LocationPicker
25975  * @param {Object} config The config object
25976  */
25977
25978
25979 Roo.bootstrap.LocationPicker = function(config){
25980     
25981     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25982     
25983     this.addEvents({
25984         /**
25985          * @event initial
25986          * Fires when the picker initialized.
25987          * @param {Roo.bootstrap.LocationPicker} this
25988          * @param {Google Location} location
25989          */
25990         initial : true,
25991         /**
25992          * @event positionchanged
25993          * Fires when the picker position changed.
25994          * @param {Roo.bootstrap.LocationPicker} this
25995          * @param {Google Location} location
25996          */
25997         positionchanged : true,
25998         /**
25999          * @event resize
26000          * Fires when the map resize.
26001          * @param {Roo.bootstrap.LocationPicker} this
26002          */
26003         resize : true,
26004         /**
26005          * @event show
26006          * Fires when the map show.
26007          * @param {Roo.bootstrap.LocationPicker} this
26008          */
26009         show : true,
26010         /**
26011          * @event hide
26012          * Fires when the map hide.
26013          * @param {Roo.bootstrap.LocationPicker} this
26014          */
26015         hide : true,
26016         /**
26017          * @event mapClick
26018          * Fires when click the map.
26019          * @param {Roo.bootstrap.LocationPicker} this
26020          * @param {Map event} e
26021          */
26022         mapClick : true,
26023         /**
26024          * @event mapRightClick
26025          * Fires when right click the map.
26026          * @param {Roo.bootstrap.LocationPicker} this
26027          * @param {Map event} e
26028          */
26029         mapRightClick : true,
26030         /**
26031          * @event markerClick
26032          * Fires when click the marker.
26033          * @param {Roo.bootstrap.LocationPicker} this
26034          * @param {Map event} e
26035          */
26036         markerClick : true,
26037         /**
26038          * @event markerRightClick
26039          * Fires when right click the marker.
26040          * @param {Roo.bootstrap.LocationPicker} this
26041          * @param {Map event} e
26042          */
26043         markerRightClick : true,
26044         /**
26045          * @event OverlayViewDraw
26046          * Fires when OverlayView Draw
26047          * @param {Roo.bootstrap.LocationPicker} this
26048          */
26049         OverlayViewDraw : true,
26050         /**
26051          * @event OverlayViewOnAdd
26052          * Fires when OverlayView Draw
26053          * @param {Roo.bootstrap.LocationPicker} this
26054          */
26055         OverlayViewOnAdd : true,
26056         /**
26057          * @event OverlayViewOnRemove
26058          * Fires when OverlayView Draw
26059          * @param {Roo.bootstrap.LocationPicker} this
26060          */
26061         OverlayViewOnRemove : true,
26062         /**
26063          * @event OverlayViewShow
26064          * Fires when OverlayView Draw
26065          * @param {Roo.bootstrap.LocationPicker} this
26066          * @param {Pixel} cpx
26067          */
26068         OverlayViewShow : true,
26069         /**
26070          * @event OverlayViewHide
26071          * Fires when OverlayView Draw
26072          * @param {Roo.bootstrap.LocationPicker} this
26073          */
26074         OverlayViewHide : true,
26075         /**
26076          * @event loadexception
26077          * Fires when load google lib failed.
26078          * @param {Roo.bootstrap.LocationPicker} this
26079          */
26080         loadexception : true
26081     });
26082         
26083 };
26084
26085 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26086     
26087     gMapContext: false,
26088     
26089     latitude: 0,
26090     longitude: 0,
26091     zoom: 15,
26092     mapTypeId: false,
26093     mapTypeControl: false,
26094     disableDoubleClickZoom: false,
26095     scrollwheel: true,
26096     streetViewControl: false,
26097     radius: 0,
26098     locationName: '',
26099     draggable: true,
26100     enableAutocomplete: false,
26101     enableReverseGeocode: true,
26102     markerTitle: '',
26103     
26104     getAutoCreate: function()
26105     {
26106
26107         var cfg = {
26108             tag: 'div',
26109             cls: 'roo-location-picker'
26110         };
26111         
26112         return cfg
26113     },
26114     
26115     initEvents: function(ct, position)
26116     {       
26117         if(!this.el.getWidth() || this.isApplied()){
26118             return;
26119         }
26120         
26121         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26122         
26123         this.initial();
26124     },
26125     
26126     initial: function()
26127     {
26128         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26129             this.fireEvent('loadexception', this);
26130             return;
26131         }
26132         
26133         if(!this.mapTypeId){
26134             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26135         }
26136         
26137         this.gMapContext = this.GMapContext();
26138         
26139         this.initOverlayView();
26140         
26141         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26142         
26143         var _this = this;
26144                 
26145         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26146             _this.setPosition(_this.gMapContext.marker.position);
26147         });
26148         
26149         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26150             _this.fireEvent('mapClick', this, event);
26151             
26152         });
26153
26154         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26155             _this.fireEvent('mapRightClick', this, event);
26156             
26157         });
26158         
26159         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26160             _this.fireEvent('markerClick', this, event);
26161             
26162         });
26163
26164         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26165             _this.fireEvent('markerRightClick', this, event);
26166             
26167         });
26168         
26169         this.setPosition(this.gMapContext.location);
26170         
26171         this.fireEvent('initial', this, this.gMapContext.location);
26172     },
26173     
26174     initOverlayView: function()
26175     {
26176         var _this = this;
26177         
26178         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26179             
26180             draw: function()
26181             {
26182                 _this.fireEvent('OverlayViewDraw', _this);
26183             },
26184             
26185             onAdd: function()
26186             {
26187                 _this.fireEvent('OverlayViewOnAdd', _this);
26188             },
26189             
26190             onRemove: function()
26191             {
26192                 _this.fireEvent('OverlayViewOnRemove', _this);
26193             },
26194             
26195             show: function(cpx)
26196             {
26197                 _this.fireEvent('OverlayViewShow', _this, cpx);
26198             },
26199             
26200             hide: function()
26201             {
26202                 _this.fireEvent('OverlayViewHide', _this);
26203             }
26204             
26205         });
26206     },
26207     
26208     fromLatLngToContainerPixel: function(event)
26209     {
26210         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26211     },
26212     
26213     isApplied: function() 
26214     {
26215         return this.getGmapContext() == false ? false : true;
26216     },
26217     
26218     getGmapContext: function() 
26219     {
26220         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26221     },
26222     
26223     GMapContext: function() 
26224     {
26225         var position = new google.maps.LatLng(this.latitude, this.longitude);
26226         
26227         var _map = new google.maps.Map(this.el.dom, {
26228             center: position,
26229             zoom: this.zoom,
26230             mapTypeId: this.mapTypeId,
26231             mapTypeControl: this.mapTypeControl,
26232             disableDoubleClickZoom: this.disableDoubleClickZoom,
26233             scrollwheel: this.scrollwheel,
26234             streetViewControl: this.streetViewControl,
26235             locationName: this.locationName,
26236             draggable: this.draggable,
26237             enableAutocomplete: this.enableAutocomplete,
26238             enableReverseGeocode: this.enableReverseGeocode
26239         });
26240         
26241         var _marker = new google.maps.Marker({
26242             position: position,
26243             map: _map,
26244             title: this.markerTitle,
26245             draggable: this.draggable
26246         });
26247         
26248         return {
26249             map: _map,
26250             marker: _marker,
26251             circle: null,
26252             location: position,
26253             radius: this.radius,
26254             locationName: this.locationName,
26255             addressComponents: {
26256                 formatted_address: null,
26257                 addressLine1: null,
26258                 addressLine2: null,
26259                 streetName: null,
26260                 streetNumber: null,
26261                 city: null,
26262                 district: null,
26263                 state: null,
26264                 stateOrProvince: null
26265             },
26266             settings: this,
26267             domContainer: this.el.dom,
26268             geodecoder: new google.maps.Geocoder()
26269         };
26270     },
26271     
26272     drawCircle: function(center, radius, options) 
26273     {
26274         if (this.gMapContext.circle != null) {
26275             this.gMapContext.circle.setMap(null);
26276         }
26277         if (radius > 0) {
26278             radius *= 1;
26279             options = Roo.apply({}, options, {
26280                 strokeColor: "#0000FF",
26281                 strokeOpacity: .35,
26282                 strokeWeight: 2,
26283                 fillColor: "#0000FF",
26284                 fillOpacity: .2
26285             });
26286             
26287             options.map = this.gMapContext.map;
26288             options.radius = radius;
26289             options.center = center;
26290             this.gMapContext.circle = new google.maps.Circle(options);
26291             return this.gMapContext.circle;
26292         }
26293         
26294         return null;
26295     },
26296     
26297     setPosition: function(location) 
26298     {
26299         this.gMapContext.location = location;
26300         this.gMapContext.marker.setPosition(location);
26301         this.gMapContext.map.panTo(location);
26302         this.drawCircle(location, this.gMapContext.radius, {});
26303         
26304         var _this = this;
26305         
26306         if (this.gMapContext.settings.enableReverseGeocode) {
26307             this.gMapContext.geodecoder.geocode({
26308                 latLng: this.gMapContext.location
26309             }, function(results, status) {
26310                 
26311                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26312                     _this.gMapContext.locationName = results[0].formatted_address;
26313                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26314                     
26315                     _this.fireEvent('positionchanged', this, location);
26316                 }
26317             });
26318             
26319             return;
26320         }
26321         
26322         this.fireEvent('positionchanged', this, location);
26323     },
26324     
26325     resize: function()
26326     {
26327         google.maps.event.trigger(this.gMapContext.map, "resize");
26328         
26329         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26330         
26331         this.fireEvent('resize', this);
26332     },
26333     
26334     setPositionByLatLng: function(latitude, longitude)
26335     {
26336         this.setPosition(new google.maps.LatLng(latitude, longitude));
26337     },
26338     
26339     getCurrentPosition: function() 
26340     {
26341         return {
26342             latitude: this.gMapContext.location.lat(),
26343             longitude: this.gMapContext.location.lng()
26344         };
26345     },
26346     
26347     getAddressName: function() 
26348     {
26349         return this.gMapContext.locationName;
26350     },
26351     
26352     getAddressComponents: function() 
26353     {
26354         return this.gMapContext.addressComponents;
26355     },
26356     
26357     address_component_from_google_geocode: function(address_components) 
26358     {
26359         var result = {};
26360         
26361         for (var i = 0; i < address_components.length; i++) {
26362             var component = address_components[i];
26363             if (component.types.indexOf("postal_code") >= 0) {
26364                 result.postalCode = component.short_name;
26365             } else if (component.types.indexOf("street_number") >= 0) {
26366                 result.streetNumber = component.short_name;
26367             } else if (component.types.indexOf("route") >= 0) {
26368                 result.streetName = component.short_name;
26369             } else if (component.types.indexOf("neighborhood") >= 0) {
26370                 result.city = component.short_name;
26371             } else if (component.types.indexOf("locality") >= 0) {
26372                 result.city = component.short_name;
26373             } else if (component.types.indexOf("sublocality") >= 0) {
26374                 result.district = component.short_name;
26375             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26376                 result.stateOrProvince = component.short_name;
26377             } else if (component.types.indexOf("country") >= 0) {
26378                 result.country = component.short_name;
26379             }
26380         }
26381         
26382         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26383         result.addressLine2 = "";
26384         return result;
26385     },
26386     
26387     setZoomLevel: function(zoom)
26388     {
26389         this.gMapContext.map.setZoom(zoom);
26390     },
26391     
26392     show: function()
26393     {
26394         if(!this.el){
26395             return;
26396         }
26397         
26398         this.el.show();
26399         
26400         this.resize();
26401         
26402         this.fireEvent('show', this);
26403     },
26404     
26405     hide: function()
26406     {
26407         if(!this.el){
26408             return;
26409         }
26410         
26411         this.el.hide();
26412         
26413         this.fireEvent('hide', this);
26414     }
26415     
26416 });
26417
26418 Roo.apply(Roo.bootstrap.LocationPicker, {
26419     
26420     OverlayView : function(map, options)
26421     {
26422         options = options || {};
26423         
26424         this.setMap(map);
26425     }
26426     
26427     
26428 });/*
26429  * - LGPL
26430  *
26431  * Alert
26432  * 
26433  */
26434
26435 /**
26436  * @class Roo.bootstrap.Alert
26437  * @extends Roo.bootstrap.Component
26438  * Bootstrap Alert class
26439  * @cfg {String} title The title of alert
26440  * @cfg {String} html The content of alert
26441  * @cfg {String} weight (  success | info | warning | danger )
26442  * @cfg {String} faicon font-awesomeicon
26443  * 
26444  * @constructor
26445  * Create a new alert
26446  * @param {Object} config The config object
26447  */
26448
26449
26450 Roo.bootstrap.Alert = function(config){
26451     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26452     
26453 };
26454
26455 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26456     
26457     title: '',
26458     html: '',
26459     weight: false,
26460     faicon: false,
26461     
26462     getAutoCreate : function()
26463     {
26464         
26465         var cfg = {
26466             tag : 'div',
26467             cls : 'alert',
26468             cn : [
26469                 {
26470                     tag : 'i',
26471                     cls : 'roo-alert-icon'
26472                     
26473                 },
26474                 {
26475                     tag : 'b',
26476                     cls : 'roo-alert-title',
26477                     html : this.title
26478                 },
26479                 {
26480                     tag : 'span',
26481                     cls : 'roo-alert-text',
26482                     html : this.html
26483                 }
26484             ]
26485         };
26486         
26487         if(this.faicon){
26488             cfg.cn[0].cls += ' fa ' + this.faicon;
26489         }
26490         
26491         if(this.weight){
26492             cfg.cls += ' alert-' + this.weight;
26493         }
26494         
26495         return cfg;
26496     },
26497     
26498     initEvents: function() 
26499     {
26500         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26501     },
26502     
26503     setTitle : function(str)
26504     {
26505         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26506     },
26507     
26508     setText : function(str)
26509     {
26510         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26511     },
26512     
26513     setWeight : function(weight)
26514     {
26515         if(this.weight){
26516             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26517         }
26518         
26519         this.weight = weight;
26520         
26521         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26522     },
26523     
26524     setIcon : function(icon)
26525     {
26526         if(this.faicon){
26527             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26528         }
26529         
26530         this.faicon = icon;
26531         
26532         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26533     },
26534     
26535     hide: function() 
26536     {
26537         this.el.hide();   
26538     },
26539     
26540     show: function() 
26541     {  
26542         this.el.show();   
26543     }
26544     
26545 });
26546
26547  
26548 /*
26549 * Licence: LGPL
26550 */
26551
26552 /**
26553  * @class Roo.bootstrap.UploadCropbox
26554  * @extends Roo.bootstrap.Component
26555  * Bootstrap UploadCropbox class
26556  * @cfg {String} emptyText show when image has been loaded
26557  * @cfg {String} rotateNotify show when image too small to rotate
26558  * @cfg {Number} errorTimeout default 3000
26559  * @cfg {Number} minWidth default 300
26560  * @cfg {Number} minHeight default 300
26561  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26562  * @cfg {Boolean} isDocument (true|false) default false
26563  * @cfg {String} url action url
26564  * @cfg {String} paramName default 'imageUpload'
26565  * @cfg {String} method default POST
26566  * @cfg {Boolean} loadMask (true|false) default true
26567  * @cfg {Boolean} loadingText default 'Loading...'
26568  * 
26569  * @constructor
26570  * Create a new UploadCropbox
26571  * @param {Object} config The config object
26572  */
26573
26574 Roo.bootstrap.UploadCropbox = function(config){
26575     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26576     
26577     this.addEvents({
26578         /**
26579          * @event beforeselectfile
26580          * Fire before select file
26581          * @param {Roo.bootstrap.UploadCropbox} this
26582          */
26583         "beforeselectfile" : true,
26584         /**
26585          * @event initial
26586          * Fire after initEvent
26587          * @param {Roo.bootstrap.UploadCropbox} this
26588          */
26589         "initial" : true,
26590         /**
26591          * @event crop
26592          * Fire after initEvent
26593          * @param {Roo.bootstrap.UploadCropbox} this
26594          * @param {String} data
26595          */
26596         "crop" : true,
26597         /**
26598          * @event prepare
26599          * Fire when preparing the file data
26600          * @param {Roo.bootstrap.UploadCropbox} this
26601          * @param {Object} file
26602          */
26603         "prepare" : true,
26604         /**
26605          * @event exception
26606          * Fire when get exception
26607          * @param {Roo.bootstrap.UploadCropbox} this
26608          * @param {XMLHttpRequest} xhr
26609          */
26610         "exception" : true,
26611         /**
26612          * @event beforeloadcanvas
26613          * Fire before load the canvas
26614          * @param {Roo.bootstrap.UploadCropbox} this
26615          * @param {String} src
26616          */
26617         "beforeloadcanvas" : true,
26618         /**
26619          * @event trash
26620          * Fire when trash image
26621          * @param {Roo.bootstrap.UploadCropbox} this
26622          */
26623         "trash" : true,
26624         /**
26625          * @event download
26626          * Fire when download the image
26627          * @param {Roo.bootstrap.UploadCropbox} this
26628          */
26629         "download" : true,
26630         /**
26631          * @event footerbuttonclick
26632          * Fire when footerbuttonclick
26633          * @param {Roo.bootstrap.UploadCropbox} this
26634          * @param {String} type
26635          */
26636         "footerbuttonclick" : true,
26637         /**
26638          * @event resize
26639          * Fire when resize
26640          * @param {Roo.bootstrap.UploadCropbox} this
26641          */
26642         "resize" : true,
26643         /**
26644          * @event rotate
26645          * Fire when rotate the image
26646          * @param {Roo.bootstrap.UploadCropbox} this
26647          * @param {String} pos
26648          */
26649         "rotate" : true,
26650         /**
26651          * @event inspect
26652          * Fire when inspect the file
26653          * @param {Roo.bootstrap.UploadCropbox} this
26654          * @param {Object} file
26655          */
26656         "inspect" : true,
26657         /**
26658          * @event upload
26659          * Fire when xhr upload the file
26660          * @param {Roo.bootstrap.UploadCropbox} this
26661          * @param {Object} data
26662          */
26663         "upload" : true,
26664         /**
26665          * @event arrange
26666          * Fire when arrange the file data
26667          * @param {Roo.bootstrap.UploadCropbox} this
26668          * @param {Object} formData
26669          */
26670         "arrange" : true
26671     });
26672     
26673     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26674 };
26675
26676 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26677     
26678     emptyText : 'Click to upload image',
26679     rotateNotify : 'Image is too small to rotate',
26680     errorTimeout : 3000,
26681     scale : 0,
26682     baseScale : 1,
26683     rotate : 0,
26684     dragable : false,
26685     pinching : false,
26686     mouseX : 0,
26687     mouseY : 0,
26688     cropData : false,
26689     minWidth : 300,
26690     minHeight : 300,
26691     file : false,
26692     exif : {},
26693     baseRotate : 1,
26694     cropType : 'image/jpeg',
26695     buttons : false,
26696     canvasLoaded : false,
26697     isDocument : false,
26698     method : 'POST',
26699     paramName : 'imageUpload',
26700     loadMask : true,
26701     loadingText : 'Loading...',
26702     maskEl : false,
26703     
26704     getAutoCreate : function()
26705     {
26706         var cfg = {
26707             tag : 'div',
26708             cls : 'roo-upload-cropbox',
26709             cn : [
26710                 {
26711                     tag : 'input',
26712                     cls : 'roo-upload-cropbox-selector',
26713                     type : 'file'
26714                 },
26715                 {
26716                     tag : 'div',
26717                     cls : 'roo-upload-cropbox-body',
26718                     style : 'cursor:pointer',
26719                     cn : [
26720                         {
26721                             tag : 'div',
26722                             cls : 'roo-upload-cropbox-preview'
26723                         },
26724                         {
26725                             tag : 'div',
26726                             cls : 'roo-upload-cropbox-thumb'
26727                         },
26728                         {
26729                             tag : 'div',
26730                             cls : 'roo-upload-cropbox-empty-notify',
26731                             html : this.emptyText
26732                         },
26733                         {
26734                             tag : 'div',
26735                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26736                             html : this.rotateNotify
26737                         }
26738                     ]
26739                 },
26740                 {
26741                     tag : 'div',
26742                     cls : 'roo-upload-cropbox-footer',
26743                     cn : {
26744                         tag : 'div',
26745                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26746                         cn : []
26747                     }
26748                 }
26749             ]
26750         };
26751         
26752         return cfg;
26753     },
26754     
26755     onRender : function(ct, position)
26756     {
26757         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26758         
26759         if (this.buttons.length) {
26760             
26761             Roo.each(this.buttons, function(bb) {
26762                 
26763                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26764                 
26765                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26766                 
26767             }, this);
26768         }
26769         
26770         if(this.loadMask){
26771             this.maskEl = this.el;
26772         }
26773     },
26774     
26775     initEvents : function()
26776     {
26777         this.urlAPI = (window.createObjectURL && window) || 
26778                                 (window.URL && URL.revokeObjectURL && URL) || 
26779                                 (window.webkitURL && webkitURL);
26780                         
26781         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26782         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26783         
26784         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26785         this.selectorEl.hide();
26786         
26787         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26788         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26789         
26790         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26791         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26792         this.thumbEl.hide();
26793         
26794         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26795         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26796         
26797         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26798         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26799         this.errorEl.hide();
26800         
26801         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26802         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26803         this.footerEl.hide();
26804         
26805         this.setThumbBoxSize();
26806         
26807         this.bind();
26808         
26809         this.resize();
26810         
26811         this.fireEvent('initial', this);
26812     },
26813
26814     bind : function()
26815     {
26816         var _this = this;
26817         
26818         window.addEventListener("resize", function() { _this.resize(); } );
26819         
26820         this.bodyEl.on('click', this.beforeSelectFile, this);
26821         
26822         if(Roo.isTouch){
26823             this.bodyEl.on('touchstart', this.onTouchStart, this);
26824             this.bodyEl.on('touchmove', this.onTouchMove, this);
26825             this.bodyEl.on('touchend', this.onTouchEnd, this);
26826         }
26827         
26828         if(!Roo.isTouch){
26829             this.bodyEl.on('mousedown', this.onMouseDown, this);
26830             this.bodyEl.on('mousemove', this.onMouseMove, this);
26831             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26832             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26833             Roo.get(document).on('mouseup', this.onMouseUp, this);
26834         }
26835         
26836         this.selectorEl.on('change', this.onFileSelected, this);
26837     },
26838     
26839     reset : function()
26840     {    
26841         this.scale = 0;
26842         this.baseScale = 1;
26843         this.rotate = 0;
26844         this.baseRotate = 1;
26845         this.dragable = false;
26846         this.pinching = false;
26847         this.mouseX = 0;
26848         this.mouseY = 0;
26849         this.cropData = false;
26850         this.notifyEl.dom.innerHTML = this.emptyText;
26851         
26852         this.selectorEl.dom.value = '';
26853         
26854     },
26855     
26856     resize : function()
26857     {
26858         if(this.fireEvent('resize', this) != false){
26859             this.setThumbBoxPosition();
26860             this.setCanvasPosition();
26861         }
26862     },
26863     
26864     onFooterButtonClick : function(e, el, o, type)
26865     {
26866         switch (type) {
26867             case 'rotate-left' :
26868                 this.onRotateLeft(e);
26869                 break;
26870             case 'rotate-right' :
26871                 this.onRotateRight(e);
26872                 break;
26873             case 'picture' :
26874                 this.beforeSelectFile(e);
26875                 break;
26876             case 'trash' :
26877                 this.trash(e);
26878                 break;
26879             case 'crop' :
26880                 this.crop(e);
26881                 break;
26882             case 'download' :
26883                 this.download(e);
26884                 break;
26885             default :
26886                 break;
26887         }
26888         
26889         this.fireEvent('footerbuttonclick', this, type);
26890     },
26891     
26892     beforeSelectFile : function(e)
26893     {
26894         e.preventDefault();
26895         
26896         if(this.fireEvent('beforeselectfile', this) != false){
26897             this.selectorEl.dom.click();
26898         }
26899     },
26900     
26901     onFileSelected : function(e)
26902     {
26903         e.preventDefault();
26904         
26905         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26906             return;
26907         }
26908         
26909         var file = this.selectorEl.dom.files[0];
26910         
26911         if(this.fireEvent('inspect', this, file) != false){
26912             this.prepare(file);
26913         }
26914         
26915     },
26916     
26917     trash : function(e)
26918     {
26919         this.fireEvent('trash', this);
26920     },
26921     
26922     download : function(e)
26923     {
26924         this.fireEvent('download', this);
26925     },
26926     
26927     loadCanvas : function(src)
26928     {   
26929         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26930             
26931             this.reset();
26932             
26933             this.imageEl = document.createElement('img');
26934             
26935             var _this = this;
26936             
26937             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26938             
26939             this.imageEl.src = src;
26940         }
26941     },
26942     
26943     onLoadCanvas : function()
26944     {   
26945         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26946         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26947         
26948         this.bodyEl.un('click', this.beforeSelectFile, this);
26949         
26950         this.notifyEl.hide();
26951         this.thumbEl.show();
26952         this.footerEl.show();
26953         
26954         this.baseRotateLevel();
26955         
26956         if(this.isDocument){
26957             this.setThumbBoxSize();
26958         }
26959         
26960         this.setThumbBoxPosition();
26961         
26962         this.baseScaleLevel();
26963         
26964         this.draw();
26965         
26966         this.resize();
26967         
26968         this.canvasLoaded = true;
26969         
26970         if(this.loadMask){
26971             this.maskEl.unmask();
26972         }
26973         
26974     },
26975     
26976     setCanvasPosition : function()
26977     {   
26978         if(!this.canvasEl){
26979             return;
26980         }
26981         
26982         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26983         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26984         
26985         this.previewEl.setLeft(pw);
26986         this.previewEl.setTop(ph);
26987         
26988     },
26989     
26990     onMouseDown : function(e)
26991     {   
26992         e.stopEvent();
26993         
26994         this.dragable = true;
26995         this.pinching = false;
26996         
26997         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26998             this.dragable = false;
26999             return;
27000         }
27001         
27002         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27003         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27004         
27005     },
27006     
27007     onMouseMove : function(e)
27008     {   
27009         e.stopEvent();
27010         
27011         if(!this.canvasLoaded){
27012             return;
27013         }
27014         
27015         if (!this.dragable){
27016             return;
27017         }
27018         
27019         var minX = Math.ceil(this.thumbEl.getLeft(true));
27020         var minY = Math.ceil(this.thumbEl.getTop(true));
27021         
27022         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27023         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27024         
27025         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27026         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27027         
27028         x = x - this.mouseX;
27029         y = y - this.mouseY;
27030         
27031         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27032         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27033         
27034         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27035         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27036         
27037         this.previewEl.setLeft(bgX);
27038         this.previewEl.setTop(bgY);
27039         
27040         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27041         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27042     },
27043     
27044     onMouseUp : function(e)
27045     {   
27046         e.stopEvent();
27047         
27048         this.dragable = false;
27049     },
27050     
27051     onMouseWheel : function(e)
27052     {   
27053         e.stopEvent();
27054         
27055         this.startScale = this.scale;
27056         
27057         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27058         
27059         if(!this.zoomable()){
27060             this.scale = this.startScale;
27061             return;
27062         }
27063         
27064         this.draw();
27065         
27066         return;
27067     },
27068     
27069     zoomable : function()
27070     {
27071         var minScale = this.thumbEl.getWidth() / this.minWidth;
27072         
27073         if(this.minWidth < this.minHeight){
27074             minScale = this.thumbEl.getHeight() / this.minHeight;
27075         }
27076         
27077         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27078         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27079         
27080         if(
27081                 this.isDocument &&
27082                 (this.rotate == 0 || this.rotate == 180) && 
27083                 (
27084                     width > this.imageEl.OriginWidth || 
27085                     height > this.imageEl.OriginHeight ||
27086                     (width < this.minWidth && height < this.minHeight)
27087                 )
27088         ){
27089             return false;
27090         }
27091         
27092         if(
27093                 this.isDocument &&
27094                 (this.rotate == 90 || this.rotate == 270) && 
27095                 (
27096                     width > this.imageEl.OriginWidth || 
27097                     height > this.imageEl.OriginHeight ||
27098                     (width < this.minHeight && height < this.minWidth)
27099                 )
27100         ){
27101             return false;
27102         }
27103         
27104         if(
27105                 !this.isDocument &&
27106                 (this.rotate == 0 || this.rotate == 180) && 
27107                 (
27108                     width < this.minWidth || 
27109                     width > this.imageEl.OriginWidth || 
27110                     height < this.minHeight || 
27111                     height > this.imageEl.OriginHeight
27112                 )
27113         ){
27114             return false;
27115         }
27116         
27117         if(
27118                 !this.isDocument &&
27119                 (this.rotate == 90 || this.rotate == 270) && 
27120                 (
27121                     width < this.minHeight || 
27122                     width > this.imageEl.OriginWidth || 
27123                     height < this.minWidth || 
27124                     height > this.imageEl.OriginHeight
27125                 )
27126         ){
27127             return false;
27128         }
27129         
27130         return true;
27131         
27132     },
27133     
27134     onRotateLeft : function(e)
27135     {   
27136         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27137             
27138             var minScale = this.thumbEl.getWidth() / this.minWidth;
27139             
27140             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27141             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27142             
27143             this.startScale = this.scale;
27144             
27145             while (this.getScaleLevel() < minScale){
27146             
27147                 this.scale = this.scale + 1;
27148                 
27149                 if(!this.zoomable()){
27150                     break;
27151                 }
27152                 
27153                 if(
27154                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27155                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27156                 ){
27157                     continue;
27158                 }
27159                 
27160                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27161
27162                 this.draw();
27163                 
27164                 return;
27165             }
27166             
27167             this.scale = this.startScale;
27168             
27169             this.onRotateFail();
27170             
27171             return false;
27172         }
27173         
27174         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27175
27176         if(this.isDocument){
27177             this.setThumbBoxSize();
27178             this.setThumbBoxPosition();
27179             this.setCanvasPosition();
27180         }
27181         
27182         this.draw();
27183         
27184         this.fireEvent('rotate', this, 'left');
27185         
27186     },
27187     
27188     onRotateRight : function(e)
27189     {
27190         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27191             
27192             var minScale = this.thumbEl.getWidth() / this.minWidth;
27193         
27194             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27195             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27196             
27197             this.startScale = this.scale;
27198             
27199             while (this.getScaleLevel() < minScale){
27200             
27201                 this.scale = this.scale + 1;
27202                 
27203                 if(!this.zoomable()){
27204                     break;
27205                 }
27206                 
27207                 if(
27208                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27209                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27210                 ){
27211                     continue;
27212                 }
27213                 
27214                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27215
27216                 this.draw();
27217                 
27218                 return;
27219             }
27220             
27221             this.scale = this.startScale;
27222             
27223             this.onRotateFail();
27224             
27225             return false;
27226         }
27227         
27228         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27229
27230         if(this.isDocument){
27231             this.setThumbBoxSize();
27232             this.setThumbBoxPosition();
27233             this.setCanvasPosition();
27234         }
27235         
27236         this.draw();
27237         
27238         this.fireEvent('rotate', this, 'right');
27239     },
27240     
27241     onRotateFail : function()
27242     {
27243         this.errorEl.show(true);
27244         
27245         var _this = this;
27246         
27247         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27248     },
27249     
27250     draw : function()
27251     {
27252         this.previewEl.dom.innerHTML = '';
27253         
27254         var canvasEl = document.createElement("canvas");
27255         
27256         var contextEl = canvasEl.getContext("2d");
27257         
27258         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27259         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27260         var center = this.imageEl.OriginWidth / 2;
27261         
27262         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27263             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27264             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27265             center = this.imageEl.OriginHeight / 2;
27266         }
27267         
27268         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27269         
27270         contextEl.translate(center, center);
27271         contextEl.rotate(this.rotate * Math.PI / 180);
27272
27273         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27274         
27275         this.canvasEl = document.createElement("canvas");
27276         
27277         this.contextEl = this.canvasEl.getContext("2d");
27278         
27279         switch (this.rotate) {
27280             case 0 :
27281                 
27282                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27283                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27284                 
27285                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27286                 
27287                 break;
27288             case 90 : 
27289                 
27290                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27291                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27292                 
27293                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27294                     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);
27295                     break;
27296                 }
27297                 
27298                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27299                 
27300                 break;
27301             case 180 :
27302                 
27303                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27304                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27305                 
27306                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27307                     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);
27308                     break;
27309                 }
27310                 
27311                 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);
27312                 
27313                 break;
27314             case 270 :
27315                 
27316                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27317                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27318         
27319                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27320                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27321                     break;
27322                 }
27323                 
27324                 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);
27325                 
27326                 break;
27327             default : 
27328                 break;
27329         }
27330         
27331         this.previewEl.appendChild(this.canvasEl);
27332         
27333         this.setCanvasPosition();
27334     },
27335     
27336     crop : function()
27337     {
27338         if(!this.canvasLoaded){
27339             return;
27340         }
27341         
27342         var imageCanvas = document.createElement("canvas");
27343         
27344         var imageContext = imageCanvas.getContext("2d");
27345         
27346         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27347         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27348         
27349         var center = imageCanvas.width / 2;
27350         
27351         imageContext.translate(center, center);
27352         
27353         imageContext.rotate(this.rotate * Math.PI / 180);
27354         
27355         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27356         
27357         var canvas = document.createElement("canvas");
27358         
27359         var context = canvas.getContext("2d");
27360                 
27361         canvas.width = this.minWidth;
27362         canvas.height = this.minHeight;
27363
27364         switch (this.rotate) {
27365             case 0 :
27366                 
27367                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27368                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27369                 
27370                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27371                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27372                 
27373                 var targetWidth = this.minWidth - 2 * x;
27374                 var targetHeight = this.minHeight - 2 * y;
27375                 
27376                 var scale = 1;
27377                 
27378                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27379                     scale = targetWidth / width;
27380                 }
27381                 
27382                 if(x > 0 && y == 0){
27383                     scale = targetHeight / height;
27384                 }
27385                 
27386                 if(x > 0 && y > 0){
27387                     scale = targetWidth / width;
27388                     
27389                     if(width < height){
27390                         scale = targetHeight / height;
27391                     }
27392                 }
27393                 
27394                 context.scale(scale, scale);
27395                 
27396                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27397                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27398
27399                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27400                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27401
27402                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27403                 
27404                 break;
27405             case 90 : 
27406                 
27407                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27408                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27409                 
27410                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27411                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27412                 
27413                 var targetWidth = this.minWidth - 2 * x;
27414                 var targetHeight = this.minHeight - 2 * y;
27415                 
27416                 var scale = 1;
27417                 
27418                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27419                     scale = targetWidth / width;
27420                 }
27421                 
27422                 if(x > 0 && y == 0){
27423                     scale = targetHeight / height;
27424                 }
27425                 
27426                 if(x > 0 && y > 0){
27427                     scale = targetWidth / width;
27428                     
27429                     if(width < height){
27430                         scale = targetHeight / height;
27431                     }
27432                 }
27433                 
27434                 context.scale(scale, scale);
27435                 
27436                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27437                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27438
27439                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27440                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27441                 
27442                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27443                 
27444                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27445                 
27446                 break;
27447             case 180 :
27448                 
27449                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27450                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27451                 
27452                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27453                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27454                 
27455                 var targetWidth = this.minWidth - 2 * x;
27456                 var targetHeight = this.minHeight - 2 * y;
27457                 
27458                 var scale = 1;
27459                 
27460                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27461                     scale = targetWidth / width;
27462                 }
27463                 
27464                 if(x > 0 && y == 0){
27465                     scale = targetHeight / height;
27466                 }
27467                 
27468                 if(x > 0 && y > 0){
27469                     scale = targetWidth / width;
27470                     
27471                     if(width < height){
27472                         scale = targetHeight / height;
27473                     }
27474                 }
27475                 
27476                 context.scale(scale, scale);
27477                 
27478                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27479                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27480
27481                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27482                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27483
27484                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27485                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27486                 
27487                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27488                 
27489                 break;
27490             case 270 :
27491                 
27492                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27493                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27494                 
27495                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27496                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27497                 
27498                 var targetWidth = this.minWidth - 2 * x;
27499                 var targetHeight = this.minHeight - 2 * y;
27500                 
27501                 var scale = 1;
27502                 
27503                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27504                     scale = targetWidth / width;
27505                 }
27506                 
27507                 if(x > 0 && y == 0){
27508                     scale = targetHeight / height;
27509                 }
27510                 
27511                 if(x > 0 && y > 0){
27512                     scale = targetWidth / width;
27513                     
27514                     if(width < height){
27515                         scale = targetHeight / height;
27516                     }
27517                 }
27518                 
27519                 context.scale(scale, scale);
27520                 
27521                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27522                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27523
27524                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27525                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27526                 
27527                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27528                 
27529                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27530                 
27531                 break;
27532             default : 
27533                 break;
27534         }
27535         
27536         this.cropData = canvas.toDataURL(this.cropType);
27537         
27538         if(this.fireEvent('crop', this, this.cropData) !== false){
27539             this.process(this.file, this.cropData);
27540         }
27541         
27542         return;
27543         
27544     },
27545     
27546     setThumbBoxSize : function()
27547     {
27548         var width, height;
27549         
27550         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27551             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27552             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27553             
27554             this.minWidth = width;
27555             this.minHeight = height;
27556             
27557             if(this.rotate == 90 || this.rotate == 270){
27558                 this.minWidth = height;
27559                 this.minHeight = width;
27560             }
27561         }
27562         
27563         height = 300;
27564         width = Math.ceil(this.minWidth * height / this.minHeight);
27565         
27566         if(this.minWidth > this.minHeight){
27567             width = 300;
27568             height = Math.ceil(this.minHeight * width / this.minWidth);
27569         }
27570         
27571         this.thumbEl.setStyle({
27572             width : width + 'px',
27573             height : height + 'px'
27574         });
27575
27576         return;
27577             
27578     },
27579     
27580     setThumbBoxPosition : function()
27581     {
27582         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27583         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27584         
27585         this.thumbEl.setLeft(x);
27586         this.thumbEl.setTop(y);
27587         
27588     },
27589     
27590     baseRotateLevel : function()
27591     {
27592         this.baseRotate = 1;
27593         
27594         if(
27595                 typeof(this.exif) != 'undefined' &&
27596                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27597                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27598         ){
27599             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27600         }
27601         
27602         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27603         
27604     },
27605     
27606     baseScaleLevel : function()
27607     {
27608         var width, height;
27609         
27610         if(this.isDocument){
27611             
27612             if(this.baseRotate == 6 || this.baseRotate == 8){
27613             
27614                 height = this.thumbEl.getHeight();
27615                 this.baseScale = height / this.imageEl.OriginWidth;
27616
27617                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27618                     width = this.thumbEl.getWidth();
27619                     this.baseScale = width / this.imageEl.OriginHeight;
27620                 }
27621
27622                 return;
27623             }
27624
27625             height = this.thumbEl.getHeight();
27626             this.baseScale = height / this.imageEl.OriginHeight;
27627
27628             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27629                 width = this.thumbEl.getWidth();
27630                 this.baseScale = width / this.imageEl.OriginWidth;
27631             }
27632
27633             return;
27634         }
27635         
27636         if(this.baseRotate == 6 || this.baseRotate == 8){
27637             
27638             width = this.thumbEl.getHeight();
27639             this.baseScale = width / this.imageEl.OriginHeight;
27640             
27641             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27642                 height = this.thumbEl.getWidth();
27643                 this.baseScale = height / this.imageEl.OriginHeight;
27644             }
27645             
27646             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27647                 height = this.thumbEl.getWidth();
27648                 this.baseScale = height / this.imageEl.OriginHeight;
27649                 
27650                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27651                     width = this.thumbEl.getHeight();
27652                     this.baseScale = width / this.imageEl.OriginWidth;
27653                 }
27654             }
27655             
27656             return;
27657         }
27658         
27659         width = this.thumbEl.getWidth();
27660         this.baseScale = width / this.imageEl.OriginWidth;
27661         
27662         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27663             height = this.thumbEl.getHeight();
27664             this.baseScale = height / this.imageEl.OriginHeight;
27665         }
27666         
27667         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27668             
27669             height = this.thumbEl.getHeight();
27670             this.baseScale = height / this.imageEl.OriginHeight;
27671             
27672             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27673                 width = this.thumbEl.getWidth();
27674                 this.baseScale = width / this.imageEl.OriginWidth;
27675             }
27676             
27677         }
27678         
27679         return;
27680     },
27681     
27682     getScaleLevel : function()
27683     {
27684         return this.baseScale * Math.pow(1.1, this.scale);
27685     },
27686     
27687     onTouchStart : function(e)
27688     {
27689         if(!this.canvasLoaded){
27690             this.beforeSelectFile(e);
27691             return;
27692         }
27693         
27694         var touches = e.browserEvent.touches;
27695         
27696         if(!touches){
27697             return;
27698         }
27699         
27700         if(touches.length == 1){
27701             this.onMouseDown(e);
27702             return;
27703         }
27704         
27705         if(touches.length != 2){
27706             return;
27707         }
27708         
27709         var coords = [];
27710         
27711         for(var i = 0, finger; finger = touches[i]; i++){
27712             coords.push(finger.pageX, finger.pageY);
27713         }
27714         
27715         var x = Math.pow(coords[0] - coords[2], 2);
27716         var y = Math.pow(coords[1] - coords[3], 2);
27717         
27718         this.startDistance = Math.sqrt(x + y);
27719         
27720         this.startScale = this.scale;
27721         
27722         this.pinching = true;
27723         this.dragable = false;
27724         
27725     },
27726     
27727     onTouchMove : function(e)
27728     {
27729         if(!this.pinching && !this.dragable){
27730             return;
27731         }
27732         
27733         var touches = e.browserEvent.touches;
27734         
27735         if(!touches){
27736             return;
27737         }
27738         
27739         if(this.dragable){
27740             this.onMouseMove(e);
27741             return;
27742         }
27743         
27744         var coords = [];
27745         
27746         for(var i = 0, finger; finger = touches[i]; i++){
27747             coords.push(finger.pageX, finger.pageY);
27748         }
27749         
27750         var x = Math.pow(coords[0] - coords[2], 2);
27751         var y = Math.pow(coords[1] - coords[3], 2);
27752         
27753         this.endDistance = Math.sqrt(x + y);
27754         
27755         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27756         
27757         if(!this.zoomable()){
27758             this.scale = this.startScale;
27759             return;
27760         }
27761         
27762         this.draw();
27763         
27764     },
27765     
27766     onTouchEnd : function(e)
27767     {
27768         this.pinching = false;
27769         this.dragable = false;
27770         
27771     },
27772     
27773     process : function(file, crop)
27774     {
27775         if(this.loadMask){
27776             this.maskEl.mask(this.loadingText);
27777         }
27778         
27779         this.xhr = new XMLHttpRequest();
27780         
27781         file.xhr = this.xhr;
27782
27783         this.xhr.open(this.method, this.url, true);
27784         
27785         var headers = {
27786             "Accept": "application/json",
27787             "Cache-Control": "no-cache",
27788             "X-Requested-With": "XMLHttpRequest"
27789         };
27790         
27791         for (var headerName in headers) {
27792             var headerValue = headers[headerName];
27793             if (headerValue) {
27794                 this.xhr.setRequestHeader(headerName, headerValue);
27795             }
27796         }
27797         
27798         var _this = this;
27799         
27800         this.xhr.onload = function()
27801         {
27802             _this.xhrOnLoad(_this.xhr);
27803         }
27804         
27805         this.xhr.onerror = function()
27806         {
27807             _this.xhrOnError(_this.xhr);
27808         }
27809         
27810         var formData = new FormData();
27811
27812         formData.append('returnHTML', 'NO');
27813         
27814         if(crop){
27815             formData.append('crop', crop);
27816         }
27817         
27818         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27819             formData.append(this.paramName, file, file.name);
27820         }
27821         
27822         if(typeof(file.filename) != 'undefined'){
27823             formData.append('filename', file.filename);
27824         }
27825         
27826         if(typeof(file.mimetype) != 'undefined'){
27827             formData.append('mimetype', file.mimetype);
27828         }
27829         
27830         if(this.fireEvent('arrange', this, formData) != false){
27831             this.xhr.send(formData);
27832         };
27833     },
27834     
27835     xhrOnLoad : function(xhr)
27836     {
27837         if(this.loadMask){
27838             this.maskEl.unmask();
27839         }
27840         
27841         if (xhr.readyState !== 4) {
27842             this.fireEvent('exception', this, xhr);
27843             return;
27844         }
27845
27846         var response = Roo.decode(xhr.responseText);
27847         
27848         if(!response.success){
27849             this.fireEvent('exception', this, xhr);
27850             return;
27851         }
27852         
27853         var response = Roo.decode(xhr.responseText);
27854         
27855         this.fireEvent('upload', this, response);
27856         
27857     },
27858     
27859     xhrOnError : function()
27860     {
27861         if(this.loadMask){
27862             this.maskEl.unmask();
27863         }
27864         
27865         Roo.log('xhr on error');
27866         
27867         var response = Roo.decode(xhr.responseText);
27868           
27869         Roo.log(response);
27870         
27871     },
27872     
27873     prepare : function(file)
27874     {   
27875         if(this.loadMask){
27876             this.maskEl.mask(this.loadingText);
27877         }
27878         
27879         this.file = false;
27880         this.exif = {};
27881         
27882         if(typeof(file) === 'string'){
27883             this.loadCanvas(file);
27884             return;
27885         }
27886         
27887         if(!file || !this.urlAPI){
27888             return;
27889         }
27890         
27891         this.file = file;
27892         this.cropType = file.type;
27893         
27894         var _this = this;
27895         
27896         if(this.fireEvent('prepare', this, this.file) != false){
27897             
27898             var reader = new FileReader();
27899             
27900             reader.onload = function (e) {
27901                 if (e.target.error) {
27902                     Roo.log(e.target.error);
27903                     return;
27904                 }
27905                 
27906                 var buffer = e.target.result,
27907                     dataView = new DataView(buffer),
27908                     offset = 2,
27909                     maxOffset = dataView.byteLength - 4,
27910                     markerBytes,
27911                     markerLength;
27912                 
27913                 if (dataView.getUint16(0) === 0xffd8) {
27914                     while (offset < maxOffset) {
27915                         markerBytes = dataView.getUint16(offset);
27916                         
27917                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27918                             markerLength = dataView.getUint16(offset + 2) + 2;
27919                             if (offset + markerLength > dataView.byteLength) {
27920                                 Roo.log('Invalid meta data: Invalid segment size.');
27921                                 break;
27922                             }
27923                             
27924                             if(markerBytes == 0xffe1){
27925                                 _this.parseExifData(
27926                                     dataView,
27927                                     offset,
27928                                     markerLength
27929                                 );
27930                             }
27931                             
27932                             offset += markerLength;
27933                             
27934                             continue;
27935                         }
27936                         
27937                         break;
27938                     }
27939                     
27940                 }
27941                 
27942                 var url = _this.urlAPI.createObjectURL(_this.file);
27943                 
27944                 _this.loadCanvas(url);
27945                 
27946                 return;
27947             }
27948             
27949             reader.readAsArrayBuffer(this.file);
27950             
27951         }
27952         
27953     },
27954     
27955     parseExifData : function(dataView, offset, length)
27956     {
27957         var tiffOffset = offset + 10,
27958             littleEndian,
27959             dirOffset;
27960     
27961         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27962             // No Exif data, might be XMP data instead
27963             return;
27964         }
27965         
27966         // Check for the ASCII code for "Exif" (0x45786966):
27967         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27968             // No Exif data, might be XMP data instead
27969             return;
27970         }
27971         if (tiffOffset + 8 > dataView.byteLength) {
27972             Roo.log('Invalid Exif data: Invalid segment size.');
27973             return;
27974         }
27975         // Check for the two null bytes:
27976         if (dataView.getUint16(offset + 8) !== 0x0000) {
27977             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27978             return;
27979         }
27980         // Check the byte alignment:
27981         switch (dataView.getUint16(tiffOffset)) {
27982         case 0x4949:
27983             littleEndian = true;
27984             break;
27985         case 0x4D4D:
27986             littleEndian = false;
27987             break;
27988         default:
27989             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27990             return;
27991         }
27992         // Check for the TIFF tag marker (0x002A):
27993         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27994             Roo.log('Invalid Exif data: Missing TIFF marker.');
27995             return;
27996         }
27997         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27998         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27999         
28000         this.parseExifTags(
28001             dataView,
28002             tiffOffset,
28003             tiffOffset + dirOffset,
28004             littleEndian
28005         );
28006     },
28007     
28008     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28009     {
28010         var tagsNumber,
28011             dirEndOffset,
28012             i;
28013         if (dirOffset + 6 > dataView.byteLength) {
28014             Roo.log('Invalid Exif data: Invalid directory offset.');
28015             return;
28016         }
28017         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28018         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28019         if (dirEndOffset + 4 > dataView.byteLength) {
28020             Roo.log('Invalid Exif data: Invalid directory size.');
28021             return;
28022         }
28023         for (i = 0; i < tagsNumber; i += 1) {
28024             this.parseExifTag(
28025                 dataView,
28026                 tiffOffset,
28027                 dirOffset + 2 + 12 * i, // tag offset
28028                 littleEndian
28029             );
28030         }
28031         // Return the offset to the next directory:
28032         return dataView.getUint32(dirEndOffset, littleEndian);
28033     },
28034     
28035     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28036     {
28037         var tag = dataView.getUint16(offset, littleEndian);
28038         
28039         this.exif[tag] = this.getExifValue(
28040             dataView,
28041             tiffOffset,
28042             offset,
28043             dataView.getUint16(offset + 2, littleEndian), // tag type
28044             dataView.getUint32(offset + 4, littleEndian), // tag length
28045             littleEndian
28046         );
28047     },
28048     
28049     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28050     {
28051         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28052             tagSize,
28053             dataOffset,
28054             values,
28055             i,
28056             str,
28057             c;
28058     
28059         if (!tagType) {
28060             Roo.log('Invalid Exif data: Invalid tag type.');
28061             return;
28062         }
28063         
28064         tagSize = tagType.size * length;
28065         // Determine if the value is contained in the dataOffset bytes,
28066         // or if the value at the dataOffset is a pointer to the actual data:
28067         dataOffset = tagSize > 4 ?
28068                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28069         if (dataOffset + tagSize > dataView.byteLength) {
28070             Roo.log('Invalid Exif data: Invalid data offset.');
28071             return;
28072         }
28073         if (length === 1) {
28074             return tagType.getValue(dataView, dataOffset, littleEndian);
28075         }
28076         values = [];
28077         for (i = 0; i < length; i += 1) {
28078             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28079         }
28080         
28081         if (tagType.ascii) {
28082             str = '';
28083             // Concatenate the chars:
28084             for (i = 0; i < values.length; i += 1) {
28085                 c = values[i];
28086                 // Ignore the terminating NULL byte(s):
28087                 if (c === '\u0000') {
28088                     break;
28089                 }
28090                 str += c;
28091             }
28092             return str;
28093         }
28094         return values;
28095     }
28096     
28097 });
28098
28099 Roo.apply(Roo.bootstrap.UploadCropbox, {
28100     tags : {
28101         'Orientation': 0x0112
28102     },
28103     
28104     Orientation: {
28105             1: 0, //'top-left',
28106 //            2: 'top-right',
28107             3: 180, //'bottom-right',
28108 //            4: 'bottom-left',
28109 //            5: 'left-top',
28110             6: 90, //'right-top',
28111 //            7: 'right-bottom',
28112             8: 270 //'left-bottom'
28113     },
28114     
28115     exifTagTypes : {
28116         // byte, 8-bit unsigned int:
28117         1: {
28118             getValue: function (dataView, dataOffset) {
28119                 return dataView.getUint8(dataOffset);
28120             },
28121             size: 1
28122         },
28123         // ascii, 8-bit byte:
28124         2: {
28125             getValue: function (dataView, dataOffset) {
28126                 return String.fromCharCode(dataView.getUint8(dataOffset));
28127             },
28128             size: 1,
28129             ascii: true
28130         },
28131         // short, 16 bit int:
28132         3: {
28133             getValue: function (dataView, dataOffset, littleEndian) {
28134                 return dataView.getUint16(dataOffset, littleEndian);
28135             },
28136             size: 2
28137         },
28138         // long, 32 bit int:
28139         4: {
28140             getValue: function (dataView, dataOffset, littleEndian) {
28141                 return dataView.getUint32(dataOffset, littleEndian);
28142             },
28143             size: 4
28144         },
28145         // rational = two long values, first is numerator, second is denominator:
28146         5: {
28147             getValue: function (dataView, dataOffset, littleEndian) {
28148                 return dataView.getUint32(dataOffset, littleEndian) /
28149                     dataView.getUint32(dataOffset + 4, littleEndian);
28150             },
28151             size: 8
28152         },
28153         // slong, 32 bit signed int:
28154         9: {
28155             getValue: function (dataView, dataOffset, littleEndian) {
28156                 return dataView.getInt32(dataOffset, littleEndian);
28157             },
28158             size: 4
28159         },
28160         // srational, two slongs, first is numerator, second is denominator:
28161         10: {
28162             getValue: function (dataView, dataOffset, littleEndian) {
28163                 return dataView.getInt32(dataOffset, littleEndian) /
28164                     dataView.getInt32(dataOffset + 4, littleEndian);
28165             },
28166             size: 8
28167         }
28168     },
28169     
28170     footer : {
28171         STANDARD : [
28172             {
28173                 tag : 'div',
28174                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28175                 action : 'rotate-left',
28176                 cn : [
28177                     {
28178                         tag : 'button',
28179                         cls : 'btn btn-default',
28180                         html : '<i class="fa fa-undo"></i>'
28181                     }
28182                 ]
28183             },
28184             {
28185                 tag : 'div',
28186                 cls : 'btn-group roo-upload-cropbox-picture',
28187                 action : 'picture',
28188                 cn : [
28189                     {
28190                         tag : 'button',
28191                         cls : 'btn btn-default',
28192                         html : '<i class="fa fa-picture-o"></i>'
28193                     }
28194                 ]
28195             },
28196             {
28197                 tag : 'div',
28198                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28199                 action : 'rotate-right',
28200                 cn : [
28201                     {
28202                         tag : 'button',
28203                         cls : 'btn btn-default',
28204                         html : '<i class="fa fa-repeat"></i>'
28205                     }
28206                 ]
28207             }
28208         ],
28209         DOCUMENT : [
28210             {
28211                 tag : 'div',
28212                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28213                 action : 'rotate-left',
28214                 cn : [
28215                     {
28216                         tag : 'button',
28217                         cls : 'btn btn-default',
28218                         html : '<i class="fa fa-undo"></i>'
28219                     }
28220                 ]
28221             },
28222             {
28223                 tag : 'div',
28224                 cls : 'btn-group roo-upload-cropbox-download',
28225                 action : 'download',
28226                 cn : [
28227                     {
28228                         tag : 'button',
28229                         cls : 'btn btn-default',
28230                         html : '<i class="fa fa-download"></i>'
28231                     }
28232                 ]
28233             },
28234             {
28235                 tag : 'div',
28236                 cls : 'btn-group roo-upload-cropbox-crop',
28237                 action : 'crop',
28238                 cn : [
28239                     {
28240                         tag : 'button',
28241                         cls : 'btn btn-default',
28242                         html : '<i class="fa fa-crop"></i>'
28243                     }
28244                 ]
28245             },
28246             {
28247                 tag : 'div',
28248                 cls : 'btn-group roo-upload-cropbox-trash',
28249                 action : 'trash',
28250                 cn : [
28251                     {
28252                         tag : 'button',
28253                         cls : 'btn btn-default',
28254                         html : '<i class="fa fa-trash"></i>'
28255                     }
28256                 ]
28257             },
28258             {
28259                 tag : 'div',
28260                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28261                 action : 'rotate-right',
28262                 cn : [
28263                     {
28264                         tag : 'button',
28265                         cls : 'btn btn-default',
28266                         html : '<i class="fa fa-repeat"></i>'
28267                     }
28268                 ]
28269             }
28270         ],
28271         ROTATOR : [
28272             {
28273                 tag : 'div',
28274                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28275                 action : 'rotate-left',
28276                 cn : [
28277                     {
28278                         tag : 'button',
28279                         cls : 'btn btn-default',
28280                         html : '<i class="fa fa-undo"></i>'
28281                     }
28282                 ]
28283             },
28284             {
28285                 tag : 'div',
28286                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28287                 action : 'rotate-right',
28288                 cn : [
28289                     {
28290                         tag : 'button',
28291                         cls : 'btn btn-default',
28292                         html : '<i class="fa fa-repeat"></i>'
28293                     }
28294                 ]
28295             }
28296         ]
28297     }
28298 });
28299
28300 /*
28301 * Licence: LGPL
28302 */
28303
28304 /**
28305  * @class Roo.bootstrap.DocumentManager
28306  * @extends Roo.bootstrap.Component
28307  * Bootstrap DocumentManager class
28308  * @cfg {String} paramName default 'imageUpload'
28309  * @cfg {String} toolTipName default 'filename'
28310  * @cfg {String} method default POST
28311  * @cfg {String} url action url
28312  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28313  * @cfg {Boolean} multiple multiple upload default true
28314  * @cfg {Number} thumbSize default 300
28315  * @cfg {String} fieldLabel
28316  * @cfg {Number} labelWidth default 4
28317  * @cfg {String} labelAlign (left|top) default left
28318  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28319 * @cfg {Number} labellg set the width of label (1-12)
28320  * @cfg {Number} labelmd set the width of label (1-12)
28321  * @cfg {Number} labelsm set the width of label (1-12)
28322  * @cfg {Number} labelxs set the width of label (1-12)
28323  * 
28324  * @constructor
28325  * Create a new DocumentManager
28326  * @param {Object} config The config object
28327  */
28328
28329 Roo.bootstrap.DocumentManager = function(config){
28330     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28331     
28332     this.files = [];
28333     this.delegates = [];
28334     
28335     this.addEvents({
28336         /**
28337          * @event initial
28338          * Fire when initial the DocumentManager
28339          * @param {Roo.bootstrap.DocumentManager} this
28340          */
28341         "initial" : true,
28342         /**
28343          * @event inspect
28344          * inspect selected file
28345          * @param {Roo.bootstrap.DocumentManager} this
28346          * @param {File} file
28347          */
28348         "inspect" : true,
28349         /**
28350          * @event exception
28351          * Fire when xhr load exception
28352          * @param {Roo.bootstrap.DocumentManager} this
28353          * @param {XMLHttpRequest} xhr
28354          */
28355         "exception" : true,
28356         /**
28357          * @event afterupload
28358          * Fire when xhr load exception
28359          * @param {Roo.bootstrap.DocumentManager} this
28360          * @param {XMLHttpRequest} xhr
28361          */
28362         "afterupload" : true,
28363         /**
28364          * @event prepare
28365          * prepare the form data
28366          * @param {Roo.bootstrap.DocumentManager} this
28367          * @param {Object} formData
28368          */
28369         "prepare" : true,
28370         /**
28371          * @event remove
28372          * Fire when remove the file
28373          * @param {Roo.bootstrap.DocumentManager} this
28374          * @param {Object} file
28375          */
28376         "remove" : true,
28377         /**
28378          * @event refresh
28379          * Fire after refresh the file
28380          * @param {Roo.bootstrap.DocumentManager} this
28381          */
28382         "refresh" : true,
28383         /**
28384          * @event click
28385          * Fire after click the image
28386          * @param {Roo.bootstrap.DocumentManager} this
28387          * @param {Object} file
28388          */
28389         "click" : true,
28390         /**
28391          * @event edit
28392          * Fire when upload a image and editable set to true
28393          * @param {Roo.bootstrap.DocumentManager} this
28394          * @param {Object} file
28395          */
28396         "edit" : true,
28397         /**
28398          * @event beforeselectfile
28399          * Fire before select file
28400          * @param {Roo.bootstrap.DocumentManager} this
28401          */
28402         "beforeselectfile" : true,
28403         /**
28404          * @event process
28405          * Fire before process file
28406          * @param {Roo.bootstrap.DocumentManager} this
28407          * @param {Object} file
28408          */
28409         "process" : true
28410         
28411     });
28412 };
28413
28414 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28415     
28416     boxes : 0,
28417     inputName : '',
28418     thumbSize : 300,
28419     multiple : true,
28420     files : false,
28421     method : 'POST',
28422     url : '',
28423     paramName : 'imageUpload',
28424     toolTipName : 'filename',
28425     fieldLabel : '',
28426     labelWidth : 4,
28427     labelAlign : 'left',
28428     editable : true,
28429     delegates : false,
28430     xhr : false, 
28431     
28432     labellg : 0,
28433     labelmd : 0,
28434     labelsm : 0,
28435     labelxs : 0,
28436     
28437     getAutoCreate : function()
28438     {   
28439         var managerWidget = {
28440             tag : 'div',
28441             cls : 'roo-document-manager',
28442             cn : [
28443                 {
28444                     tag : 'input',
28445                     cls : 'roo-document-manager-selector',
28446                     type : 'file'
28447                 },
28448                 {
28449                     tag : 'div',
28450                     cls : 'roo-document-manager-uploader',
28451                     cn : [
28452                         {
28453                             tag : 'div',
28454                             cls : 'roo-document-manager-upload-btn',
28455                             html : '<i class="fa fa-plus"></i>'
28456                         }
28457                     ]
28458                     
28459                 }
28460             ]
28461         };
28462         
28463         var content = [
28464             {
28465                 tag : 'div',
28466                 cls : 'column col-md-12',
28467                 cn : managerWidget
28468             }
28469         ];
28470         
28471         if(this.fieldLabel.length){
28472             
28473             content = [
28474                 {
28475                     tag : 'div',
28476                     cls : 'column col-md-12',
28477                     html : this.fieldLabel
28478                 },
28479                 {
28480                     tag : 'div',
28481                     cls : 'column col-md-12',
28482                     cn : managerWidget
28483                 }
28484             ];
28485
28486             if(this.labelAlign == 'left'){
28487                 content = [
28488                     {
28489                         tag : 'div',
28490                         cls : 'column',
28491                         html : this.fieldLabel
28492                     },
28493                     {
28494                         tag : 'div',
28495                         cls : 'column',
28496                         cn : managerWidget
28497                     }
28498                 ];
28499                 
28500                 if(this.labelWidth > 12){
28501                     content[0].style = "width: " + this.labelWidth + 'px';
28502                 }
28503
28504                 if(this.labelWidth < 13 && this.labelmd == 0){
28505                     this.labelmd = this.labelWidth;
28506                 }
28507
28508                 if(this.labellg > 0){
28509                     content[0].cls += ' col-lg-' + this.labellg;
28510                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28511                 }
28512
28513                 if(this.labelmd > 0){
28514                     content[0].cls += ' col-md-' + this.labelmd;
28515                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28516                 }
28517
28518                 if(this.labelsm > 0){
28519                     content[0].cls += ' col-sm-' + this.labelsm;
28520                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28521                 }
28522
28523                 if(this.labelxs > 0){
28524                     content[0].cls += ' col-xs-' + this.labelxs;
28525                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28526                 }
28527                 
28528             }
28529         }
28530         
28531         var cfg = {
28532             tag : 'div',
28533             cls : 'row clearfix',
28534             cn : content
28535         };
28536         
28537         return cfg;
28538         
28539     },
28540     
28541     initEvents : function()
28542     {
28543         this.managerEl = this.el.select('.roo-document-manager', true).first();
28544         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28545         
28546         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28547         this.selectorEl.hide();
28548         
28549         if(this.multiple){
28550             this.selectorEl.attr('multiple', 'multiple');
28551         }
28552         
28553         this.selectorEl.on('change', this.onFileSelected, this);
28554         
28555         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28556         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28557         
28558         this.uploader.on('click', this.onUploaderClick, this);
28559         
28560         this.renderProgressDialog();
28561         
28562         var _this = this;
28563         
28564         window.addEventListener("resize", function() { _this.refresh(); } );
28565         
28566         this.fireEvent('initial', this);
28567     },
28568     
28569     renderProgressDialog : function()
28570     {
28571         var _this = this;
28572         
28573         this.progressDialog = new Roo.bootstrap.Modal({
28574             cls : 'roo-document-manager-progress-dialog',
28575             allow_close : false,
28576             title : '',
28577             buttons : [
28578                 {
28579                     name  :'cancel',
28580                     weight : 'danger',
28581                     html : 'Cancel'
28582                 }
28583             ], 
28584             listeners : { 
28585                 btnclick : function() {
28586                     _this.uploadCancel();
28587                     this.hide();
28588                 }
28589             }
28590         });
28591          
28592         this.progressDialog.render(Roo.get(document.body));
28593          
28594         this.progress = new Roo.bootstrap.Progress({
28595             cls : 'roo-document-manager-progress',
28596             active : true,
28597             striped : true
28598         });
28599         
28600         this.progress.render(this.progressDialog.getChildContainer());
28601         
28602         this.progressBar = new Roo.bootstrap.ProgressBar({
28603             cls : 'roo-document-manager-progress-bar',
28604             aria_valuenow : 0,
28605             aria_valuemin : 0,
28606             aria_valuemax : 12,
28607             panel : 'success'
28608         });
28609         
28610         this.progressBar.render(this.progress.getChildContainer());
28611     },
28612     
28613     onUploaderClick : function(e)
28614     {
28615         e.preventDefault();
28616      
28617         if(this.fireEvent('beforeselectfile', this) != false){
28618             this.selectorEl.dom.click();
28619         }
28620         
28621     },
28622     
28623     onFileSelected : function(e)
28624     {
28625         e.preventDefault();
28626         
28627         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28628             return;
28629         }
28630         
28631         Roo.each(this.selectorEl.dom.files, function(file){
28632             if(this.fireEvent('inspect', this, file) != false){
28633                 this.files.push(file);
28634             }
28635         }, this);
28636         
28637         this.queue();
28638         
28639     },
28640     
28641     queue : function()
28642     {
28643         this.selectorEl.dom.value = '';
28644         
28645         if(!this.files.length){
28646             return;
28647         }
28648         
28649         if(this.boxes > 0 && this.files.length > this.boxes){
28650             this.files = this.files.slice(0, this.boxes);
28651         }
28652         
28653         this.uploader.show();
28654         
28655         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28656             this.uploader.hide();
28657         }
28658         
28659         var _this = this;
28660         
28661         var files = [];
28662         
28663         var docs = [];
28664         
28665         Roo.each(this.files, function(file){
28666             
28667             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28668                 var f = this.renderPreview(file);
28669                 files.push(f);
28670                 return;
28671             }
28672             
28673             if(file.type.indexOf('image') != -1){
28674                 this.delegates.push(
28675                     (function(){
28676                         _this.process(file);
28677                     }).createDelegate(this)
28678                 );
28679         
28680                 return;
28681             }
28682             
28683             docs.push(
28684                 (function(){
28685                     _this.process(file);
28686                 }).createDelegate(this)
28687             );
28688             
28689         }, this);
28690         
28691         this.files = files;
28692         
28693         this.delegates = this.delegates.concat(docs);
28694         
28695         if(!this.delegates.length){
28696             this.refresh();
28697             return;
28698         }
28699         
28700         this.progressBar.aria_valuemax = this.delegates.length;
28701         
28702         this.arrange();
28703         
28704         return;
28705     },
28706     
28707     arrange : function()
28708     {
28709         if(!this.delegates.length){
28710             this.progressDialog.hide();
28711             this.refresh();
28712             return;
28713         }
28714         
28715         var delegate = this.delegates.shift();
28716         
28717         this.progressDialog.show();
28718         
28719         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28720         
28721         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28722         
28723         delegate();
28724     },
28725     
28726     refresh : function()
28727     {
28728         this.uploader.show();
28729         
28730         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28731             this.uploader.hide();
28732         }
28733         
28734         Roo.isTouch ? this.closable(false) : this.closable(true);
28735         
28736         this.fireEvent('refresh', this);
28737     },
28738     
28739     onRemove : function(e, el, o)
28740     {
28741         e.preventDefault();
28742         
28743         this.fireEvent('remove', this, o);
28744         
28745     },
28746     
28747     remove : function(o)
28748     {
28749         var files = [];
28750         
28751         Roo.each(this.files, function(file){
28752             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28753                 files.push(file);
28754                 return;
28755             }
28756
28757             o.target.remove();
28758
28759         }, this);
28760         
28761         this.files = files;
28762         
28763         this.refresh();
28764     },
28765     
28766     clear : function()
28767     {
28768         Roo.each(this.files, function(file){
28769             if(!file.target){
28770                 return;
28771             }
28772             
28773             file.target.remove();
28774
28775         }, this);
28776         
28777         this.files = [];
28778         
28779         this.refresh();
28780     },
28781     
28782     onClick : function(e, el, o)
28783     {
28784         e.preventDefault();
28785         
28786         this.fireEvent('click', this, o);
28787         
28788     },
28789     
28790     closable : function(closable)
28791     {
28792         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28793             
28794             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28795             
28796             if(closable){
28797                 el.show();
28798                 return;
28799             }
28800             
28801             el.hide();
28802             
28803         }, this);
28804     },
28805     
28806     xhrOnLoad : function(xhr)
28807     {
28808         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28809             el.remove();
28810         }, this);
28811         
28812         if (xhr.readyState !== 4) {
28813             this.arrange();
28814             this.fireEvent('exception', this, xhr);
28815             return;
28816         }
28817
28818         var response = Roo.decode(xhr.responseText);
28819         
28820         if(!response.success){
28821             this.arrange();
28822             this.fireEvent('exception', this, xhr);
28823             return;
28824         }
28825         
28826         var file = this.renderPreview(response.data);
28827         
28828         this.files.push(file);
28829         
28830         this.arrange();
28831         
28832         this.fireEvent('afterupload', this, xhr);
28833         
28834     },
28835     
28836     xhrOnError : function(xhr)
28837     {
28838         Roo.log('xhr on error');
28839         
28840         var response = Roo.decode(xhr.responseText);
28841           
28842         Roo.log(response);
28843         
28844         this.arrange();
28845     },
28846     
28847     process : function(file)
28848     {
28849         if(this.fireEvent('process', this, file) !== false){
28850             if(this.editable && file.type.indexOf('image') != -1){
28851                 this.fireEvent('edit', this, file);
28852                 return;
28853             }
28854
28855             this.uploadStart(file, false);
28856
28857             return;
28858         }
28859         
28860     },
28861     
28862     uploadStart : function(file, crop)
28863     {
28864         this.xhr = new XMLHttpRequest();
28865         
28866         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28867             this.arrange();
28868             return;
28869         }
28870         
28871         file.xhr = this.xhr;
28872             
28873         this.managerEl.createChild({
28874             tag : 'div',
28875             cls : 'roo-document-manager-loading',
28876             cn : [
28877                 {
28878                     tag : 'div',
28879                     tooltip : file.name,
28880                     cls : 'roo-document-manager-thumb',
28881                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28882                 }
28883             ]
28884
28885         });
28886
28887         this.xhr.open(this.method, this.url, true);
28888         
28889         var headers = {
28890             "Accept": "application/json",
28891             "Cache-Control": "no-cache",
28892             "X-Requested-With": "XMLHttpRequest"
28893         };
28894         
28895         for (var headerName in headers) {
28896             var headerValue = headers[headerName];
28897             if (headerValue) {
28898                 this.xhr.setRequestHeader(headerName, headerValue);
28899             }
28900         }
28901         
28902         var _this = this;
28903         
28904         this.xhr.onload = function()
28905         {
28906             _this.xhrOnLoad(_this.xhr);
28907         }
28908         
28909         this.xhr.onerror = function()
28910         {
28911             _this.xhrOnError(_this.xhr);
28912         }
28913         
28914         var formData = new FormData();
28915
28916         formData.append('returnHTML', 'NO');
28917         
28918         if(crop){
28919             formData.append('crop', crop);
28920         }
28921         
28922         formData.append(this.paramName, file, file.name);
28923         
28924         var options = {
28925             file : file, 
28926             manually : false
28927         };
28928         
28929         if(this.fireEvent('prepare', this, formData, options) != false){
28930             
28931             if(options.manually){
28932                 return;
28933             }
28934             
28935             this.xhr.send(formData);
28936             return;
28937         };
28938         
28939         this.uploadCancel();
28940     },
28941     
28942     uploadCancel : function()
28943     {
28944         if (this.xhr) {
28945             this.xhr.abort();
28946         }
28947         
28948         this.delegates = [];
28949         
28950         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28951             el.remove();
28952         }, this);
28953         
28954         this.arrange();
28955     },
28956     
28957     renderPreview : function(file)
28958     {
28959         if(typeof(file.target) != 'undefined' && file.target){
28960             return file;
28961         }
28962         
28963         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28964         
28965         var previewEl = this.managerEl.createChild({
28966             tag : 'div',
28967             cls : 'roo-document-manager-preview',
28968             cn : [
28969                 {
28970                     tag : 'div',
28971                     tooltip : file[this.toolTipName],
28972                     cls : 'roo-document-manager-thumb',
28973                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28974                 },
28975                 {
28976                     tag : 'button',
28977                     cls : 'close',
28978                     html : '<i class="fa fa-times-circle"></i>'
28979                 }
28980             ]
28981         });
28982
28983         var close = previewEl.select('button.close', true).first();
28984
28985         close.on('click', this.onRemove, this, file);
28986
28987         file.target = previewEl;
28988
28989         var image = previewEl.select('img', true).first();
28990         
28991         var _this = this;
28992         
28993         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28994         
28995         image.on('click', this.onClick, this, file);
28996         
28997         return file;
28998         
28999     },
29000     
29001     onPreviewLoad : function(file, image)
29002     {
29003         if(typeof(file.target) == 'undefined' || !file.target){
29004             return;
29005         }
29006         
29007         var width = image.dom.naturalWidth || image.dom.width;
29008         var height = image.dom.naturalHeight || image.dom.height;
29009         
29010         if(width > height){
29011             file.target.addClass('wide');
29012             return;
29013         }
29014         
29015         file.target.addClass('tall');
29016         return;
29017         
29018     },
29019     
29020     uploadFromSource : function(file, crop)
29021     {
29022         this.xhr = new XMLHttpRequest();
29023         
29024         this.managerEl.createChild({
29025             tag : 'div',
29026             cls : 'roo-document-manager-loading',
29027             cn : [
29028                 {
29029                     tag : 'div',
29030                     tooltip : file.name,
29031                     cls : 'roo-document-manager-thumb',
29032                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29033                 }
29034             ]
29035
29036         });
29037
29038         this.xhr.open(this.method, this.url, true);
29039         
29040         var headers = {
29041             "Accept": "application/json",
29042             "Cache-Control": "no-cache",
29043             "X-Requested-With": "XMLHttpRequest"
29044         };
29045         
29046         for (var headerName in headers) {
29047             var headerValue = headers[headerName];
29048             if (headerValue) {
29049                 this.xhr.setRequestHeader(headerName, headerValue);
29050             }
29051         }
29052         
29053         var _this = this;
29054         
29055         this.xhr.onload = function()
29056         {
29057             _this.xhrOnLoad(_this.xhr);
29058         }
29059         
29060         this.xhr.onerror = function()
29061         {
29062             _this.xhrOnError(_this.xhr);
29063         }
29064         
29065         var formData = new FormData();
29066
29067         formData.append('returnHTML', 'NO');
29068         
29069         formData.append('crop', crop);
29070         
29071         if(typeof(file.filename) != 'undefined'){
29072             formData.append('filename', file.filename);
29073         }
29074         
29075         if(typeof(file.mimetype) != 'undefined'){
29076             formData.append('mimetype', file.mimetype);
29077         }
29078         
29079         Roo.log(formData);
29080         
29081         if(this.fireEvent('prepare', this, formData) != false){
29082             this.xhr.send(formData);
29083         };
29084     }
29085 });
29086
29087 /*
29088 * Licence: LGPL
29089 */
29090
29091 /**
29092  * @class Roo.bootstrap.DocumentViewer
29093  * @extends Roo.bootstrap.Component
29094  * Bootstrap DocumentViewer class
29095  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29096  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29097  * 
29098  * @constructor
29099  * Create a new DocumentViewer
29100  * @param {Object} config The config object
29101  */
29102
29103 Roo.bootstrap.DocumentViewer = function(config){
29104     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29105     
29106     this.addEvents({
29107         /**
29108          * @event initial
29109          * Fire after initEvent
29110          * @param {Roo.bootstrap.DocumentViewer} this
29111          */
29112         "initial" : true,
29113         /**
29114          * @event click
29115          * Fire after click
29116          * @param {Roo.bootstrap.DocumentViewer} this
29117          */
29118         "click" : true,
29119         /**
29120          * @event download
29121          * Fire after download button
29122          * @param {Roo.bootstrap.DocumentViewer} this
29123          */
29124         "download" : true,
29125         /**
29126          * @event trash
29127          * Fire after trash button
29128          * @param {Roo.bootstrap.DocumentViewer} this
29129          */
29130         "trash" : true
29131         
29132     });
29133 };
29134
29135 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29136     
29137     showDownload : true,
29138     
29139     showTrash : true,
29140     
29141     getAutoCreate : function()
29142     {
29143         var cfg = {
29144             tag : 'div',
29145             cls : 'roo-document-viewer',
29146             cn : [
29147                 {
29148                     tag : 'div',
29149                     cls : 'roo-document-viewer-body',
29150                     cn : [
29151                         {
29152                             tag : 'div',
29153                             cls : 'roo-document-viewer-thumb',
29154                             cn : [
29155                                 {
29156                                     tag : 'img',
29157                                     cls : 'roo-document-viewer-image'
29158                                 }
29159                             ]
29160                         }
29161                     ]
29162                 },
29163                 {
29164                     tag : 'div',
29165                     cls : 'roo-document-viewer-footer',
29166                     cn : {
29167                         tag : 'div',
29168                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29169                         cn : [
29170                             {
29171                                 tag : 'div',
29172                                 cls : 'btn-group roo-document-viewer-download',
29173                                 cn : [
29174                                     {
29175                                         tag : 'button',
29176                                         cls : 'btn btn-default',
29177                                         html : '<i class="fa fa-download"></i>'
29178                                     }
29179                                 ]
29180                             },
29181                             {
29182                                 tag : 'div',
29183                                 cls : 'btn-group roo-document-viewer-trash',
29184                                 cn : [
29185                                     {
29186                                         tag : 'button',
29187                                         cls : 'btn btn-default',
29188                                         html : '<i class="fa fa-trash"></i>'
29189                                     }
29190                                 ]
29191                             }
29192                         ]
29193                     }
29194                 }
29195             ]
29196         };
29197         
29198         return cfg;
29199     },
29200     
29201     initEvents : function()
29202     {
29203         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29204         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29205         
29206         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29207         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29208         
29209         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29210         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29211         
29212         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29213         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29214         
29215         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29216         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29217         
29218         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29219         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29220         
29221         this.bodyEl.on('click', this.onClick, this);
29222         this.downloadBtn.on('click', this.onDownload, this);
29223         this.trashBtn.on('click', this.onTrash, this);
29224         
29225         this.downloadBtn.hide();
29226         this.trashBtn.hide();
29227         
29228         if(this.showDownload){
29229             this.downloadBtn.show();
29230         }
29231         
29232         if(this.showTrash){
29233             this.trashBtn.show();
29234         }
29235         
29236         if(!this.showDownload && !this.showTrash) {
29237             this.footerEl.hide();
29238         }
29239         
29240     },
29241     
29242     initial : function()
29243     {
29244         this.fireEvent('initial', this);
29245         
29246     },
29247     
29248     onClick : function(e)
29249     {
29250         e.preventDefault();
29251         
29252         this.fireEvent('click', this);
29253     },
29254     
29255     onDownload : function(e)
29256     {
29257         e.preventDefault();
29258         
29259         this.fireEvent('download', this);
29260     },
29261     
29262     onTrash : function(e)
29263     {
29264         e.preventDefault();
29265         
29266         this.fireEvent('trash', this);
29267     }
29268     
29269 });
29270 /*
29271  * - LGPL
29272  *
29273  * nav progress bar
29274  * 
29275  */
29276
29277 /**
29278  * @class Roo.bootstrap.NavProgressBar
29279  * @extends Roo.bootstrap.Component
29280  * Bootstrap NavProgressBar class
29281  * 
29282  * @constructor
29283  * Create a new nav progress bar
29284  * @param {Object} config The config object
29285  */
29286
29287 Roo.bootstrap.NavProgressBar = function(config){
29288     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29289
29290     this.bullets = this.bullets || [];
29291    
29292 //    Roo.bootstrap.NavProgressBar.register(this);
29293      this.addEvents({
29294         /**
29295              * @event changed
29296              * Fires when the active item changes
29297              * @param {Roo.bootstrap.NavProgressBar} this
29298              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29299              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29300          */
29301         'changed': true
29302      });
29303     
29304 };
29305
29306 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29307     
29308     bullets : [],
29309     barItems : [],
29310     
29311     getAutoCreate : function()
29312     {
29313         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29314         
29315         cfg = {
29316             tag : 'div',
29317             cls : 'roo-navigation-bar-group',
29318             cn : [
29319                 {
29320                     tag : 'div',
29321                     cls : 'roo-navigation-top-bar'
29322                 },
29323                 {
29324                     tag : 'div',
29325                     cls : 'roo-navigation-bullets-bar',
29326                     cn : [
29327                         {
29328                             tag : 'ul',
29329                             cls : 'roo-navigation-bar'
29330                         }
29331                     ]
29332                 },
29333                 
29334                 {
29335                     tag : 'div',
29336                     cls : 'roo-navigation-bottom-bar'
29337                 }
29338             ]
29339             
29340         };
29341         
29342         return cfg;
29343         
29344     },
29345     
29346     initEvents: function() 
29347     {
29348         
29349     },
29350     
29351     onRender : function(ct, position) 
29352     {
29353         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29354         
29355         if(this.bullets.length){
29356             Roo.each(this.bullets, function(b){
29357                this.addItem(b);
29358             }, this);
29359         }
29360         
29361         this.format();
29362         
29363     },
29364     
29365     addItem : function(cfg)
29366     {
29367         var item = new Roo.bootstrap.NavProgressItem(cfg);
29368         
29369         item.parentId = this.id;
29370         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29371         
29372         if(cfg.html){
29373             var top = new Roo.bootstrap.Element({
29374                 tag : 'div',
29375                 cls : 'roo-navigation-bar-text'
29376             });
29377             
29378             var bottom = new Roo.bootstrap.Element({
29379                 tag : 'div',
29380                 cls : 'roo-navigation-bar-text'
29381             });
29382             
29383             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29384             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29385             
29386             var topText = new Roo.bootstrap.Element({
29387                 tag : 'span',
29388                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29389             });
29390             
29391             var bottomText = new Roo.bootstrap.Element({
29392                 tag : 'span',
29393                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29394             });
29395             
29396             topText.onRender(top.el, null);
29397             bottomText.onRender(bottom.el, null);
29398             
29399             item.topEl = top;
29400             item.bottomEl = bottom;
29401         }
29402         
29403         this.barItems.push(item);
29404         
29405         return item;
29406     },
29407     
29408     getActive : function()
29409     {
29410         var active = false;
29411         
29412         Roo.each(this.barItems, function(v){
29413             
29414             if (!v.isActive()) {
29415                 return;
29416             }
29417             
29418             active = v;
29419             return false;
29420             
29421         });
29422         
29423         return active;
29424     },
29425     
29426     setActiveItem : function(item)
29427     {
29428         var prev = false;
29429         
29430         Roo.each(this.barItems, function(v){
29431             if (v.rid == item.rid) {
29432                 return ;
29433             }
29434             
29435             if (v.isActive()) {
29436                 v.setActive(false);
29437                 prev = v;
29438             }
29439         });
29440
29441         item.setActive(true);
29442         
29443         this.fireEvent('changed', this, item, prev);
29444     },
29445     
29446     getBarItem: function(rid)
29447     {
29448         var ret = false;
29449         
29450         Roo.each(this.barItems, function(e) {
29451             if (e.rid != rid) {
29452                 return;
29453             }
29454             
29455             ret =  e;
29456             return false;
29457         });
29458         
29459         return ret;
29460     },
29461     
29462     indexOfItem : function(item)
29463     {
29464         var index = false;
29465         
29466         Roo.each(this.barItems, function(v, i){
29467             
29468             if (v.rid != item.rid) {
29469                 return;
29470             }
29471             
29472             index = i;
29473             return false
29474         });
29475         
29476         return index;
29477     },
29478     
29479     setActiveNext : function()
29480     {
29481         var i = this.indexOfItem(this.getActive());
29482         
29483         if (i > this.barItems.length) {
29484             return;
29485         }
29486         
29487         this.setActiveItem(this.barItems[i+1]);
29488     },
29489     
29490     setActivePrev : function()
29491     {
29492         var i = this.indexOfItem(this.getActive());
29493         
29494         if (i  < 1) {
29495             return;
29496         }
29497         
29498         this.setActiveItem(this.barItems[i-1]);
29499     },
29500     
29501     format : function()
29502     {
29503         if(!this.barItems.length){
29504             return;
29505         }
29506      
29507         var width = 100 / this.barItems.length;
29508         
29509         Roo.each(this.barItems, function(i){
29510             i.el.setStyle('width', width + '%');
29511             i.topEl.el.setStyle('width', width + '%');
29512             i.bottomEl.el.setStyle('width', width + '%');
29513         }, this);
29514         
29515     }
29516     
29517 });
29518 /*
29519  * - LGPL
29520  *
29521  * Nav Progress Item
29522  * 
29523  */
29524
29525 /**
29526  * @class Roo.bootstrap.NavProgressItem
29527  * @extends Roo.bootstrap.Component
29528  * Bootstrap NavProgressItem class
29529  * @cfg {String} rid the reference id
29530  * @cfg {Boolean} active (true|false) Is item active default false
29531  * @cfg {Boolean} disabled (true|false) Is item active default false
29532  * @cfg {String} html
29533  * @cfg {String} position (top|bottom) text position default bottom
29534  * @cfg {String} icon show icon instead of number
29535  * 
29536  * @constructor
29537  * Create a new NavProgressItem
29538  * @param {Object} config The config object
29539  */
29540 Roo.bootstrap.NavProgressItem = function(config){
29541     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29542     this.addEvents({
29543         // raw events
29544         /**
29545          * @event click
29546          * The raw click event for the entire grid.
29547          * @param {Roo.bootstrap.NavProgressItem} this
29548          * @param {Roo.EventObject} e
29549          */
29550         "click" : true
29551     });
29552    
29553 };
29554
29555 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29556     
29557     rid : '',
29558     active : false,
29559     disabled : false,
29560     html : '',
29561     position : 'bottom',
29562     icon : false,
29563     
29564     getAutoCreate : function()
29565     {
29566         var iconCls = 'roo-navigation-bar-item-icon';
29567         
29568         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29569         
29570         var cfg = {
29571             tag: 'li',
29572             cls: 'roo-navigation-bar-item',
29573             cn : [
29574                 {
29575                     tag : 'i',
29576                     cls : iconCls
29577                 }
29578             ]
29579         };
29580         
29581         if(this.active){
29582             cfg.cls += ' active';
29583         }
29584         if(this.disabled){
29585             cfg.cls += ' disabled';
29586         }
29587         
29588         return cfg;
29589     },
29590     
29591     disable : function()
29592     {
29593         this.setDisabled(true);
29594     },
29595     
29596     enable : function()
29597     {
29598         this.setDisabled(false);
29599     },
29600     
29601     initEvents: function() 
29602     {
29603         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29604         
29605         this.iconEl.on('click', this.onClick, this);
29606     },
29607     
29608     onClick : function(e)
29609     {
29610         e.preventDefault();
29611         
29612         if(this.disabled){
29613             return;
29614         }
29615         
29616         if(this.fireEvent('click', this, e) === false){
29617             return;
29618         };
29619         
29620         this.parent().setActiveItem(this);
29621     },
29622     
29623     isActive: function () 
29624     {
29625         return this.active;
29626     },
29627     
29628     setActive : function(state)
29629     {
29630         if(this.active == state){
29631             return;
29632         }
29633         
29634         this.active = state;
29635         
29636         if (state) {
29637             this.el.addClass('active');
29638             return;
29639         }
29640         
29641         this.el.removeClass('active');
29642         
29643         return;
29644     },
29645     
29646     setDisabled : function(state)
29647     {
29648         if(this.disabled == state){
29649             return;
29650         }
29651         
29652         this.disabled = state;
29653         
29654         if (state) {
29655             this.el.addClass('disabled');
29656             return;
29657         }
29658         
29659         this.el.removeClass('disabled');
29660     },
29661     
29662     tooltipEl : function()
29663     {
29664         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29665     }
29666 });
29667  
29668
29669  /*
29670  * - LGPL
29671  *
29672  * FieldLabel
29673  * 
29674  */
29675
29676 /**
29677  * @class Roo.bootstrap.FieldLabel
29678  * @extends Roo.bootstrap.Component
29679  * Bootstrap FieldLabel class
29680  * @cfg {String} html contents of the element
29681  * @cfg {String} tag tag of the element default label
29682  * @cfg {String} cls class of the element
29683  * @cfg {String} target label target 
29684  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29685  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29686  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29687  * @cfg {String} iconTooltip default "This field is required"
29688  * 
29689  * @constructor
29690  * Create a new FieldLabel
29691  * @param {Object} config The config object
29692  */
29693
29694 Roo.bootstrap.FieldLabel = function(config){
29695     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29696     
29697     this.addEvents({
29698             /**
29699              * @event invalid
29700              * Fires after the field has been marked as invalid.
29701              * @param {Roo.form.FieldLabel} this
29702              * @param {String} msg The validation message
29703              */
29704             invalid : true,
29705             /**
29706              * @event valid
29707              * Fires after the field has been validated with no errors.
29708              * @param {Roo.form.FieldLabel} this
29709              */
29710             valid : true
29711         });
29712 };
29713
29714 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29715     
29716     tag: 'label',
29717     cls: '',
29718     html: '',
29719     target: '',
29720     allowBlank : true,
29721     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29722     validClass : 'text-success fa fa-lg fa-check',
29723     iconTooltip : 'This field is required',
29724     
29725     getAutoCreate : function(){
29726         
29727         var cfg = {
29728             tag : this.tag,
29729             cls : 'roo-bootstrap-field-label ' + this.cls,
29730             for : this.target,
29731             cn : [
29732                 {
29733                     tag : 'i',
29734                     cls : '',
29735                     tooltip : this.iconTooltip
29736                 },
29737                 {
29738                     tag : 'span',
29739                     html : this.html
29740                 }
29741             ] 
29742         };
29743         
29744         return cfg;
29745     },
29746     
29747     initEvents: function() 
29748     {
29749         Roo.bootstrap.Element.superclass.initEvents.call(this);
29750         
29751         this.iconEl = this.el.select('i', true).first();
29752         
29753         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29754         
29755         Roo.bootstrap.FieldLabel.register(this);
29756     },
29757     
29758     /**
29759      * Mark this field as valid
29760      */
29761     markValid : function()
29762     {
29763         this.iconEl.show();
29764         
29765         this.iconEl.removeClass(this.invalidClass);
29766         
29767         this.iconEl.addClass(this.validClass);
29768         
29769         this.fireEvent('valid', this);
29770     },
29771     
29772     /**
29773      * Mark this field as invalid
29774      * @param {String} msg The validation message
29775      */
29776     markInvalid : function(msg)
29777     {
29778         this.iconEl.show();
29779         
29780         this.iconEl.removeClass(this.validClass);
29781         
29782         this.iconEl.addClass(this.invalidClass);
29783         
29784         this.fireEvent('invalid', this, msg);
29785     }
29786     
29787    
29788 });
29789
29790 Roo.apply(Roo.bootstrap.FieldLabel, {
29791     
29792     groups: {},
29793     
29794      /**
29795     * register a FieldLabel Group
29796     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29797     */
29798     register : function(label)
29799     {
29800         if(this.groups.hasOwnProperty(label.target)){
29801             return;
29802         }
29803      
29804         this.groups[label.target] = label;
29805         
29806     },
29807     /**
29808     * fetch a FieldLabel Group based on the target
29809     * @param {string} target
29810     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29811     */
29812     get: function(target) {
29813         if (typeof(this.groups[target]) == 'undefined') {
29814             return false;
29815         }
29816         
29817         return this.groups[target] ;
29818     }
29819 });
29820
29821  
29822
29823  /*
29824  * - LGPL
29825  *
29826  * page DateSplitField.
29827  * 
29828  */
29829
29830
29831 /**
29832  * @class Roo.bootstrap.DateSplitField
29833  * @extends Roo.bootstrap.Component
29834  * Bootstrap DateSplitField class
29835  * @cfg {string} fieldLabel - the label associated
29836  * @cfg {Number} labelWidth set the width of label (0-12)
29837  * @cfg {String} labelAlign (top|left)
29838  * @cfg {Boolean} dayAllowBlank (true|false) default false
29839  * @cfg {Boolean} monthAllowBlank (true|false) default false
29840  * @cfg {Boolean} yearAllowBlank (true|false) default false
29841  * @cfg {string} dayPlaceholder 
29842  * @cfg {string} monthPlaceholder
29843  * @cfg {string} yearPlaceholder
29844  * @cfg {string} dayFormat default 'd'
29845  * @cfg {string} monthFormat default 'm'
29846  * @cfg {string} yearFormat default 'Y'
29847  * @cfg {Number} labellg set the width of label (1-12)
29848  * @cfg {Number} labelmd set the width of label (1-12)
29849  * @cfg {Number} labelsm set the width of label (1-12)
29850  * @cfg {Number} labelxs set the width of label (1-12)
29851
29852  *     
29853  * @constructor
29854  * Create a new DateSplitField
29855  * @param {Object} config The config object
29856  */
29857
29858 Roo.bootstrap.DateSplitField = function(config){
29859     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29860     
29861     this.addEvents({
29862         // raw events
29863          /**
29864          * @event years
29865          * getting the data of years
29866          * @param {Roo.bootstrap.DateSplitField} this
29867          * @param {Object} years
29868          */
29869         "years" : true,
29870         /**
29871          * @event days
29872          * getting the data of days
29873          * @param {Roo.bootstrap.DateSplitField} this
29874          * @param {Object} days
29875          */
29876         "days" : true,
29877         /**
29878          * @event invalid
29879          * Fires after the field has been marked as invalid.
29880          * @param {Roo.form.Field} this
29881          * @param {String} msg The validation message
29882          */
29883         invalid : true,
29884        /**
29885          * @event valid
29886          * Fires after the field has been validated with no errors.
29887          * @param {Roo.form.Field} this
29888          */
29889         valid : true
29890     });
29891 };
29892
29893 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29894     
29895     fieldLabel : '',
29896     labelAlign : 'top',
29897     labelWidth : 3,
29898     dayAllowBlank : false,
29899     monthAllowBlank : false,
29900     yearAllowBlank : false,
29901     dayPlaceholder : '',
29902     monthPlaceholder : '',
29903     yearPlaceholder : '',
29904     dayFormat : 'd',
29905     monthFormat : 'm',
29906     yearFormat : 'Y',
29907     isFormField : true,
29908     labellg : 0,
29909     labelmd : 0,
29910     labelsm : 0,
29911     labelxs : 0,
29912     
29913     getAutoCreate : function()
29914     {
29915         var cfg = {
29916             tag : 'div',
29917             cls : 'row roo-date-split-field-group',
29918             cn : [
29919                 {
29920                     tag : 'input',
29921                     type : 'hidden',
29922                     cls : 'form-hidden-field roo-date-split-field-group-value',
29923                     name : this.name
29924                 }
29925             ]
29926         };
29927         
29928         var labelCls = 'col-md-12';
29929         var contentCls = 'col-md-4';
29930         
29931         if(this.fieldLabel){
29932             
29933             var label = {
29934                 tag : 'div',
29935                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29936                 cn : [
29937                     {
29938                         tag : 'label',
29939                         html : this.fieldLabel
29940                     }
29941                 ]
29942             };
29943             
29944             if(this.labelAlign == 'left'){
29945             
29946                 if(this.labelWidth > 12){
29947                     label.style = "width: " + this.labelWidth + 'px';
29948                 }
29949
29950                 if(this.labelWidth < 13 && this.labelmd == 0){
29951                     this.labelmd = this.labelWidth;
29952                 }
29953
29954                 if(this.labellg > 0){
29955                     labelCls = ' col-lg-' + this.labellg;
29956                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29957                 }
29958
29959                 if(this.labelmd > 0){
29960                     labelCls = ' col-md-' + this.labelmd;
29961                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29962                 }
29963
29964                 if(this.labelsm > 0){
29965                     labelCls = ' col-sm-' + this.labelsm;
29966                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29967                 }
29968
29969                 if(this.labelxs > 0){
29970                     labelCls = ' col-xs-' + this.labelxs;
29971                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29972                 }
29973             }
29974             
29975             label.cls += ' ' + labelCls;
29976             
29977             cfg.cn.push(label);
29978         }
29979         
29980         Roo.each(['day', 'month', 'year'], function(t){
29981             cfg.cn.push({
29982                 tag : 'div',
29983                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29984             });
29985         }, this);
29986         
29987         return cfg;
29988     },
29989     
29990     inputEl: function ()
29991     {
29992         return this.el.select('.roo-date-split-field-group-value', true).first();
29993     },
29994     
29995     onRender : function(ct, position) 
29996     {
29997         var _this = this;
29998         
29999         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30000         
30001         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30002         
30003         this.dayField = new Roo.bootstrap.ComboBox({
30004             allowBlank : this.dayAllowBlank,
30005             alwaysQuery : true,
30006             displayField : 'value',
30007             editable : false,
30008             fieldLabel : '',
30009             forceSelection : true,
30010             mode : 'local',
30011             placeholder : this.dayPlaceholder,
30012             selectOnFocus : true,
30013             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30014             triggerAction : 'all',
30015             typeAhead : true,
30016             valueField : 'value',
30017             store : new Roo.data.SimpleStore({
30018                 data : (function() {    
30019                     var days = [];
30020                     _this.fireEvent('days', _this, days);
30021                     return days;
30022                 })(),
30023                 fields : [ 'value' ]
30024             }),
30025             listeners : {
30026                 select : function (_self, record, index)
30027                 {
30028                     _this.setValue(_this.getValue());
30029                 }
30030             }
30031         });
30032
30033         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30034         
30035         this.monthField = new Roo.bootstrap.MonthField({
30036             after : '<i class=\"fa fa-calendar\"></i>',
30037             allowBlank : this.monthAllowBlank,
30038             placeholder : this.monthPlaceholder,
30039             readOnly : true,
30040             listeners : {
30041                 render : function (_self)
30042                 {
30043                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30044                         e.preventDefault();
30045                         _self.focus();
30046                     });
30047                 },
30048                 select : function (_self, oldvalue, newvalue)
30049                 {
30050                     _this.setValue(_this.getValue());
30051                 }
30052             }
30053         });
30054         
30055         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30056         
30057         this.yearField = new Roo.bootstrap.ComboBox({
30058             allowBlank : this.yearAllowBlank,
30059             alwaysQuery : true,
30060             displayField : 'value',
30061             editable : false,
30062             fieldLabel : '',
30063             forceSelection : true,
30064             mode : 'local',
30065             placeholder : this.yearPlaceholder,
30066             selectOnFocus : true,
30067             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30068             triggerAction : 'all',
30069             typeAhead : true,
30070             valueField : 'value',
30071             store : new Roo.data.SimpleStore({
30072                 data : (function() {
30073                     var years = [];
30074                     _this.fireEvent('years', _this, years);
30075                     return years;
30076                 })(),
30077                 fields : [ 'value' ]
30078             }),
30079             listeners : {
30080                 select : function (_self, record, index)
30081                 {
30082                     _this.setValue(_this.getValue());
30083                 }
30084             }
30085         });
30086
30087         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30088     },
30089     
30090     setValue : function(v, format)
30091     {
30092         this.inputEl.dom.value = v;
30093         
30094         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30095         
30096         var d = Date.parseDate(v, f);
30097         
30098         if(!d){
30099             this.validate();
30100             return;
30101         }
30102         
30103         this.setDay(d.format(this.dayFormat));
30104         this.setMonth(d.format(this.monthFormat));
30105         this.setYear(d.format(this.yearFormat));
30106         
30107         this.validate();
30108         
30109         return;
30110     },
30111     
30112     setDay : function(v)
30113     {
30114         this.dayField.setValue(v);
30115         this.inputEl.dom.value = this.getValue();
30116         this.validate();
30117         return;
30118     },
30119     
30120     setMonth : function(v)
30121     {
30122         this.monthField.setValue(v, true);
30123         this.inputEl.dom.value = this.getValue();
30124         this.validate();
30125         return;
30126     },
30127     
30128     setYear : function(v)
30129     {
30130         this.yearField.setValue(v);
30131         this.inputEl.dom.value = this.getValue();
30132         this.validate();
30133         return;
30134     },
30135     
30136     getDay : function()
30137     {
30138         return this.dayField.getValue();
30139     },
30140     
30141     getMonth : function()
30142     {
30143         return this.monthField.getValue();
30144     },
30145     
30146     getYear : function()
30147     {
30148         return this.yearField.getValue();
30149     },
30150     
30151     getValue : function()
30152     {
30153         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30154         
30155         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30156         
30157         return date;
30158     },
30159     
30160     reset : function()
30161     {
30162         this.setDay('');
30163         this.setMonth('');
30164         this.setYear('');
30165         this.inputEl.dom.value = '';
30166         this.validate();
30167         return;
30168     },
30169     
30170     validate : function()
30171     {
30172         var d = this.dayField.validate();
30173         var m = this.monthField.validate();
30174         var y = this.yearField.validate();
30175         
30176         var valid = true;
30177         
30178         if(
30179                 (!this.dayAllowBlank && !d) ||
30180                 (!this.monthAllowBlank && !m) ||
30181                 (!this.yearAllowBlank && !y)
30182         ){
30183             valid = false;
30184         }
30185         
30186         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30187             return valid;
30188         }
30189         
30190         if(valid){
30191             this.markValid();
30192             return valid;
30193         }
30194         
30195         this.markInvalid();
30196         
30197         return valid;
30198     },
30199     
30200     markValid : function()
30201     {
30202         
30203         var label = this.el.select('label', true).first();
30204         var icon = this.el.select('i.fa-star', true).first();
30205
30206         if(label && icon){
30207             icon.remove();
30208         }
30209         
30210         this.fireEvent('valid', this);
30211     },
30212     
30213      /**
30214      * Mark this field as invalid
30215      * @param {String} msg The validation message
30216      */
30217     markInvalid : function(msg)
30218     {
30219         
30220         var label = this.el.select('label', true).first();
30221         var icon = this.el.select('i.fa-star', true).first();
30222
30223         if(label && !icon){
30224             this.el.select('.roo-date-split-field-label', true).createChild({
30225                 tag : 'i',
30226                 cls : 'text-danger fa fa-lg fa-star',
30227                 tooltip : 'This field is required',
30228                 style : 'margin-right:5px;'
30229             }, label, true);
30230         }
30231         
30232         this.fireEvent('invalid', this, msg);
30233     },
30234     
30235     clearInvalid : function()
30236     {
30237         var label = this.el.select('label', true).first();
30238         var icon = this.el.select('i.fa-star', true).first();
30239
30240         if(label && icon){
30241             icon.remove();
30242         }
30243         
30244         this.fireEvent('valid', this);
30245     },
30246     
30247     getName: function()
30248     {
30249         return this.name;
30250     }
30251     
30252 });
30253
30254  /**
30255  *
30256  * This is based on 
30257  * http://masonry.desandro.com
30258  *
30259  * The idea is to render all the bricks based on vertical width...
30260  *
30261  * The original code extends 'outlayer' - we might need to use that....
30262  * 
30263  */
30264
30265
30266 /**
30267  * @class Roo.bootstrap.LayoutMasonry
30268  * @extends Roo.bootstrap.Component
30269  * Bootstrap Layout Masonry class
30270  * 
30271  * @constructor
30272  * Create a new Element
30273  * @param {Object} config The config object
30274  */
30275
30276 Roo.bootstrap.LayoutMasonry = function(config){
30277     
30278     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30279     
30280     this.bricks = [];
30281     
30282     Roo.bootstrap.LayoutMasonry.register(this);
30283     
30284     this.addEvents({
30285         // raw events
30286         /**
30287          * @event layout
30288          * Fire after layout the items
30289          * @param {Roo.bootstrap.LayoutMasonry} this
30290          * @param {Roo.EventObject} e
30291          */
30292         "layout" : true
30293     });
30294     
30295 };
30296
30297 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30298     
30299     /**
30300      * @cfg {Boolean} isLayoutInstant = no animation?
30301      */   
30302     isLayoutInstant : false, // needed?
30303    
30304     /**
30305      * @cfg {Number} boxWidth  width of the columns
30306      */   
30307     boxWidth : 450,
30308     
30309       /**
30310      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30311      */   
30312     boxHeight : 0,
30313     
30314     /**
30315      * @cfg {Number} padWidth padding below box..
30316      */   
30317     padWidth : 10, 
30318     
30319     /**
30320      * @cfg {Number} gutter gutter width..
30321      */   
30322     gutter : 10,
30323     
30324      /**
30325      * @cfg {Number} maxCols maximum number of columns
30326      */   
30327     
30328     maxCols: 0,
30329     
30330     /**
30331      * @cfg {Boolean} isAutoInitial defalut true
30332      */   
30333     isAutoInitial : true, 
30334     
30335     containerWidth: 0,
30336     
30337     /**
30338      * @cfg {Boolean} isHorizontal defalut false
30339      */   
30340     isHorizontal : false, 
30341
30342     currentSize : null,
30343     
30344     tag: 'div',
30345     
30346     cls: '',
30347     
30348     bricks: null, //CompositeElement
30349     
30350     cols : 1,
30351     
30352     _isLayoutInited : false,
30353     
30354 //    isAlternative : false, // only use for vertical layout...
30355     
30356     /**
30357      * @cfg {Number} alternativePadWidth padding below box..
30358      */   
30359     alternativePadWidth : 50,
30360     
30361     selectedBrick : [],
30362     
30363     getAutoCreate : function(){
30364         
30365         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30366         
30367         var cfg = {
30368             tag: this.tag,
30369             cls: 'blog-masonary-wrapper ' + this.cls,
30370             cn : {
30371                 cls : 'mas-boxes masonary'
30372             }
30373         };
30374         
30375         return cfg;
30376     },
30377     
30378     getChildContainer: function( )
30379     {
30380         if (this.boxesEl) {
30381             return this.boxesEl;
30382         }
30383         
30384         this.boxesEl = this.el.select('.mas-boxes').first();
30385         
30386         return this.boxesEl;
30387     },
30388     
30389     
30390     initEvents : function()
30391     {
30392         var _this = this;
30393         
30394         if(this.isAutoInitial){
30395             Roo.log('hook children rendered');
30396             this.on('childrenrendered', function() {
30397                 Roo.log('children rendered');
30398                 _this.initial();
30399             } ,this);
30400         }
30401     },
30402     
30403     initial : function()
30404     {
30405         this.selectedBrick = [];
30406         
30407         this.currentSize = this.el.getBox(true);
30408         
30409         Roo.EventManager.onWindowResize(this.resize, this); 
30410
30411         if(!this.isAutoInitial){
30412             this.layout();
30413             return;
30414         }
30415         
30416         this.layout();
30417         
30418         return;
30419         //this.layout.defer(500,this);
30420         
30421     },
30422     
30423     resize : function()
30424     {
30425         var cs = this.el.getBox(true);
30426         
30427         if (
30428                 this.currentSize.width == cs.width && 
30429                 this.currentSize.x == cs.x && 
30430                 this.currentSize.height == cs.height && 
30431                 this.currentSize.y == cs.y 
30432         ) {
30433             Roo.log("no change in with or X or Y");
30434             return;
30435         }
30436         
30437         this.currentSize = cs;
30438         
30439         this.layout();
30440         
30441     },
30442     
30443     layout : function()
30444     {   
30445         this._resetLayout();
30446         
30447         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30448         
30449         this.layoutItems( isInstant );
30450       
30451         this._isLayoutInited = true;
30452         
30453         this.fireEvent('layout', this);
30454         
30455     },
30456     
30457     _resetLayout : function()
30458     {
30459         if(this.isHorizontal){
30460             this.horizontalMeasureColumns();
30461             return;
30462         }
30463         
30464         this.verticalMeasureColumns();
30465         
30466     },
30467     
30468     verticalMeasureColumns : function()
30469     {
30470         this.getContainerWidth();
30471         
30472 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30473 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30474 //            return;
30475 //        }
30476         
30477         var boxWidth = this.boxWidth + this.padWidth;
30478         
30479         if(this.containerWidth < this.boxWidth){
30480             boxWidth = this.containerWidth
30481         }
30482         
30483         var containerWidth = this.containerWidth;
30484         
30485         var cols = Math.floor(containerWidth / boxWidth);
30486         
30487         this.cols = Math.max( cols, 1 );
30488         
30489         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30490         
30491         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30492         
30493         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30494         
30495         this.colWidth = boxWidth + avail - this.padWidth;
30496         
30497         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30498         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30499     },
30500     
30501     horizontalMeasureColumns : function()
30502     {
30503         this.getContainerWidth();
30504         
30505         var boxWidth = this.boxWidth;
30506         
30507         if(this.containerWidth < boxWidth){
30508             boxWidth = this.containerWidth;
30509         }
30510         
30511         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30512         
30513         this.el.setHeight(boxWidth);
30514         
30515     },
30516     
30517     getContainerWidth : function()
30518     {
30519         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30520     },
30521     
30522     layoutItems : function( isInstant )
30523     {
30524         Roo.log(this.bricks);
30525         
30526         var items = Roo.apply([], this.bricks);
30527         
30528         if(this.isHorizontal){
30529             this._horizontalLayoutItems( items , isInstant );
30530             return;
30531         }
30532         
30533 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30534 //            this._verticalAlternativeLayoutItems( items , isInstant );
30535 //            return;
30536 //        }
30537         
30538         this._verticalLayoutItems( items , isInstant );
30539         
30540     },
30541     
30542     _verticalLayoutItems : function ( items , isInstant)
30543     {
30544         if ( !items || !items.length ) {
30545             return;
30546         }
30547         
30548         var standard = [
30549             ['xs', 'xs', 'xs', 'tall'],
30550             ['xs', 'xs', 'tall'],
30551             ['xs', 'xs', 'sm'],
30552             ['xs', 'xs', 'xs'],
30553             ['xs', 'tall'],
30554             ['xs', 'sm'],
30555             ['xs', 'xs'],
30556             ['xs'],
30557             
30558             ['sm', 'xs', 'xs'],
30559             ['sm', 'xs'],
30560             ['sm'],
30561             
30562             ['tall', 'xs', 'xs', 'xs'],
30563             ['tall', 'xs', 'xs'],
30564             ['tall', 'xs'],
30565             ['tall']
30566             
30567         ];
30568         
30569         var queue = [];
30570         
30571         var boxes = [];
30572         
30573         var box = [];
30574         
30575         Roo.each(items, function(item, k){
30576             
30577             switch (item.size) {
30578                 // these layouts take up a full box,
30579                 case 'md' :
30580                 case 'md-left' :
30581                 case 'md-right' :
30582                 case 'wide' :
30583                     
30584                     if(box.length){
30585                         boxes.push(box);
30586                         box = [];
30587                     }
30588                     
30589                     boxes.push([item]);
30590                     
30591                     break;
30592                     
30593                 case 'xs' :
30594                 case 'sm' :
30595                 case 'tall' :
30596                     
30597                     box.push(item);
30598                     
30599                     break;
30600                 default :
30601                     break;
30602                     
30603             }
30604             
30605         }, this);
30606         
30607         if(box.length){
30608             boxes.push(box);
30609             box = [];
30610         }
30611         
30612         var filterPattern = function(box, length)
30613         {
30614             if(!box.length){
30615                 return;
30616             }
30617             
30618             var match = false;
30619             
30620             var pattern = box.slice(0, length);
30621             
30622             var format = [];
30623             
30624             Roo.each(pattern, function(i){
30625                 format.push(i.size);
30626             }, this);
30627             
30628             Roo.each(standard, function(s){
30629                 
30630                 if(String(s) != String(format)){
30631                     return;
30632                 }
30633                 
30634                 match = true;
30635                 return false;
30636                 
30637             }, this);
30638             
30639             if(!match && length == 1){
30640                 return;
30641             }
30642             
30643             if(!match){
30644                 filterPattern(box, length - 1);
30645                 return;
30646             }
30647                 
30648             queue.push(pattern);
30649
30650             box = box.slice(length, box.length);
30651
30652             filterPattern(box, 4);
30653
30654             return;
30655             
30656         }
30657         
30658         Roo.each(boxes, function(box, k){
30659             
30660             if(!box.length){
30661                 return;
30662             }
30663             
30664             if(box.length == 1){
30665                 queue.push(box);
30666                 return;
30667             }
30668             
30669             filterPattern(box, 4);
30670             
30671         }, this);
30672         
30673         this._processVerticalLayoutQueue( queue, isInstant );
30674         
30675     },
30676     
30677 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30678 //    {
30679 //        if ( !items || !items.length ) {
30680 //            return;
30681 //        }
30682 //
30683 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30684 //        
30685 //    },
30686     
30687     _horizontalLayoutItems : function ( items , isInstant)
30688     {
30689         if ( !items || !items.length || items.length < 3) {
30690             return;
30691         }
30692         
30693         items.reverse();
30694         
30695         var eItems = items.slice(0, 3);
30696         
30697         items = items.slice(3, items.length);
30698         
30699         var standard = [
30700             ['xs', 'xs', 'xs', 'wide'],
30701             ['xs', 'xs', 'wide'],
30702             ['xs', 'xs', 'sm'],
30703             ['xs', 'xs', 'xs'],
30704             ['xs', 'wide'],
30705             ['xs', 'sm'],
30706             ['xs', 'xs'],
30707             ['xs'],
30708             
30709             ['sm', 'xs', 'xs'],
30710             ['sm', 'xs'],
30711             ['sm'],
30712             
30713             ['wide', 'xs', 'xs', 'xs'],
30714             ['wide', 'xs', 'xs'],
30715             ['wide', 'xs'],
30716             ['wide'],
30717             
30718             ['wide-thin']
30719         ];
30720         
30721         var queue = [];
30722         
30723         var boxes = [];
30724         
30725         var box = [];
30726         
30727         Roo.each(items, function(item, k){
30728             
30729             switch (item.size) {
30730                 case 'md' :
30731                 case 'md-left' :
30732                 case 'md-right' :
30733                 case 'tall' :
30734                     
30735                     if(box.length){
30736                         boxes.push(box);
30737                         box = [];
30738                     }
30739                     
30740                     boxes.push([item]);
30741                     
30742                     break;
30743                     
30744                 case 'xs' :
30745                 case 'sm' :
30746                 case 'wide' :
30747                 case 'wide-thin' :
30748                     
30749                     box.push(item);
30750                     
30751                     break;
30752                 default :
30753                     break;
30754                     
30755             }
30756             
30757         }, this);
30758         
30759         if(box.length){
30760             boxes.push(box);
30761             box = [];
30762         }
30763         
30764         var filterPattern = function(box, length)
30765         {
30766             if(!box.length){
30767                 return;
30768             }
30769             
30770             var match = false;
30771             
30772             var pattern = box.slice(0, length);
30773             
30774             var format = [];
30775             
30776             Roo.each(pattern, function(i){
30777                 format.push(i.size);
30778             }, this);
30779             
30780             Roo.each(standard, function(s){
30781                 
30782                 if(String(s) != String(format)){
30783                     return;
30784                 }
30785                 
30786                 match = true;
30787                 return false;
30788                 
30789             }, this);
30790             
30791             if(!match && length == 1){
30792                 return;
30793             }
30794             
30795             if(!match){
30796                 filterPattern(box, length - 1);
30797                 return;
30798             }
30799                 
30800             queue.push(pattern);
30801
30802             box = box.slice(length, box.length);
30803
30804             filterPattern(box, 4);
30805
30806             return;
30807             
30808         }
30809         
30810         Roo.each(boxes, function(box, k){
30811             
30812             if(!box.length){
30813                 return;
30814             }
30815             
30816             if(box.length == 1){
30817                 queue.push(box);
30818                 return;
30819             }
30820             
30821             filterPattern(box, 4);
30822             
30823         }, this);
30824         
30825         
30826         var prune = [];
30827         
30828         var pos = this.el.getBox(true);
30829         
30830         var minX = pos.x;
30831         
30832         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30833         
30834         var hit_end = false;
30835         
30836         Roo.each(queue, function(box){
30837             
30838             if(hit_end){
30839                 
30840                 Roo.each(box, function(b){
30841                 
30842                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30843                     b.el.hide();
30844
30845                 }, this);
30846
30847                 return;
30848             }
30849             
30850             var mx = 0;
30851             
30852             Roo.each(box, function(b){
30853                 
30854                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30855                 b.el.show();
30856
30857                 mx = Math.max(mx, b.x);
30858                 
30859             }, this);
30860             
30861             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30862             
30863             if(maxX < minX){
30864                 
30865                 Roo.each(box, function(b){
30866                 
30867                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30868                     b.el.hide();
30869                     
30870                 }, this);
30871                 
30872                 hit_end = true;
30873                 
30874                 return;
30875             }
30876             
30877             prune.push(box);
30878             
30879         }, this);
30880         
30881         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30882     },
30883     
30884     /** Sets position of item in DOM
30885     * @param {Element} item
30886     * @param {Number} x - horizontal position
30887     * @param {Number} y - vertical position
30888     * @param {Boolean} isInstant - disables transitions
30889     */
30890     _processVerticalLayoutQueue : function( queue, isInstant )
30891     {
30892         var pos = this.el.getBox(true);
30893         var x = pos.x;
30894         var y = pos.y;
30895         var maxY = [];
30896         
30897         for (var i = 0; i < this.cols; i++){
30898             maxY[i] = pos.y;
30899         }
30900         
30901         Roo.each(queue, function(box, k){
30902             
30903             var col = k % this.cols;
30904             
30905             Roo.each(box, function(b,kk){
30906                 
30907                 b.el.position('absolute');
30908                 
30909                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30910                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30911                 
30912                 if(b.size == 'md-left' || b.size == 'md-right'){
30913                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30914                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30915                 }
30916                 
30917                 b.el.setWidth(width);
30918                 b.el.setHeight(height);
30919                 // iframe?
30920                 b.el.select('iframe',true).setSize(width,height);
30921                 
30922             }, this);
30923             
30924             for (var i = 0; i < this.cols; i++){
30925                 
30926                 if(maxY[i] < maxY[col]){
30927                     col = i;
30928                     continue;
30929                 }
30930                 
30931                 col = Math.min(col, i);
30932                 
30933             }
30934             
30935             x = pos.x + col * (this.colWidth + this.padWidth);
30936             
30937             y = maxY[col];
30938             
30939             var positions = [];
30940             
30941             switch (box.length){
30942                 case 1 :
30943                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30944                     break;
30945                 case 2 :
30946                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30947                     break;
30948                 case 3 :
30949                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30950                     break;
30951                 case 4 :
30952                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30953                     break;
30954                 default :
30955                     break;
30956             }
30957             
30958             Roo.each(box, function(b,kk){
30959                 
30960                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30961                 
30962                 var sz = b.el.getSize();
30963                 
30964                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30965                 
30966             }, this);
30967             
30968         }, this);
30969         
30970         var mY = 0;
30971         
30972         for (var i = 0; i < this.cols; i++){
30973             mY = Math.max(mY, maxY[i]);
30974         }
30975         
30976         this.el.setHeight(mY - pos.y);
30977         
30978     },
30979     
30980 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30981 //    {
30982 //        var pos = this.el.getBox(true);
30983 //        var x = pos.x;
30984 //        var y = pos.y;
30985 //        var maxX = pos.right;
30986 //        
30987 //        var maxHeight = 0;
30988 //        
30989 //        Roo.each(items, function(item, k){
30990 //            
30991 //            var c = k % 2;
30992 //            
30993 //            item.el.position('absolute');
30994 //                
30995 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30996 //
30997 //            item.el.setWidth(width);
30998 //
30999 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31000 //
31001 //            item.el.setHeight(height);
31002 //            
31003 //            if(c == 0){
31004 //                item.el.setXY([x, y], isInstant ? false : true);
31005 //            } else {
31006 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31007 //            }
31008 //            
31009 //            y = y + height + this.alternativePadWidth;
31010 //            
31011 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31012 //            
31013 //        }, this);
31014 //        
31015 //        this.el.setHeight(maxHeight);
31016 //        
31017 //    },
31018     
31019     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31020     {
31021         var pos = this.el.getBox(true);
31022         
31023         var minX = pos.x;
31024         var minY = pos.y;
31025         
31026         var maxX = pos.right;
31027         
31028         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31029         
31030         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31031         
31032         Roo.each(queue, function(box, k){
31033             
31034             Roo.each(box, function(b, kk){
31035                 
31036                 b.el.position('absolute');
31037                 
31038                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31039                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31040                 
31041                 if(b.size == 'md-left' || b.size == 'md-right'){
31042                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31043                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31044                 }
31045                 
31046                 b.el.setWidth(width);
31047                 b.el.setHeight(height);
31048                 
31049             }, this);
31050             
31051             if(!box.length){
31052                 return;
31053             }
31054             
31055             var positions = [];
31056             
31057             switch (box.length){
31058                 case 1 :
31059                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31060                     break;
31061                 case 2 :
31062                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31063                     break;
31064                 case 3 :
31065                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31066                     break;
31067                 case 4 :
31068                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31069                     break;
31070                 default :
31071                     break;
31072             }
31073             
31074             Roo.each(box, function(b,kk){
31075                 
31076                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31077                 
31078                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31079                 
31080             }, this);
31081             
31082         }, this);
31083         
31084     },
31085     
31086     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31087     {
31088         Roo.each(eItems, function(b,k){
31089             
31090             b.size = (k == 0) ? 'sm' : 'xs';
31091             b.x = (k == 0) ? 2 : 1;
31092             b.y = (k == 0) ? 2 : 1;
31093             
31094             b.el.position('absolute');
31095             
31096             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31097                 
31098             b.el.setWidth(width);
31099             
31100             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31101             
31102             b.el.setHeight(height);
31103             
31104         }, this);
31105
31106         var positions = [];
31107         
31108         positions.push({
31109             x : maxX - this.unitWidth * 2 - this.gutter,
31110             y : minY
31111         });
31112         
31113         positions.push({
31114             x : maxX - this.unitWidth,
31115             y : minY + (this.unitWidth + this.gutter) * 2
31116         });
31117         
31118         positions.push({
31119             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31120             y : minY
31121         });
31122         
31123         Roo.each(eItems, function(b,k){
31124             
31125             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31126
31127         }, this);
31128         
31129     },
31130     
31131     getVerticalOneBoxColPositions : function(x, y, box)
31132     {
31133         var pos = [];
31134         
31135         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31136         
31137         if(box[0].size == 'md-left'){
31138             rand = 0;
31139         }
31140         
31141         if(box[0].size == 'md-right'){
31142             rand = 1;
31143         }
31144         
31145         pos.push({
31146             x : x + (this.unitWidth + this.gutter) * rand,
31147             y : y
31148         });
31149         
31150         return pos;
31151     },
31152     
31153     getVerticalTwoBoxColPositions : function(x, y, box)
31154     {
31155         var pos = [];
31156         
31157         if(box[0].size == 'xs'){
31158             
31159             pos.push({
31160                 x : x,
31161                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31162             });
31163
31164             pos.push({
31165                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31166                 y : y
31167             });
31168             
31169             return pos;
31170             
31171         }
31172         
31173         pos.push({
31174             x : x,
31175             y : y
31176         });
31177
31178         pos.push({
31179             x : x + (this.unitWidth + this.gutter) * 2,
31180             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31181         });
31182         
31183         return pos;
31184         
31185     },
31186     
31187     getVerticalThreeBoxColPositions : function(x, y, box)
31188     {
31189         var pos = [];
31190         
31191         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31192             
31193             pos.push({
31194                 x : x,
31195                 y : y
31196             });
31197
31198             pos.push({
31199                 x : x + (this.unitWidth + this.gutter) * 1,
31200                 y : y
31201             });
31202             
31203             pos.push({
31204                 x : x + (this.unitWidth + this.gutter) * 2,
31205                 y : y
31206             });
31207             
31208             return pos;
31209             
31210         }
31211         
31212         if(box[0].size == 'xs' && box[1].size == 'xs'){
31213             
31214             pos.push({
31215                 x : x,
31216                 y : y
31217             });
31218
31219             pos.push({
31220                 x : x,
31221                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31222             });
31223             
31224             pos.push({
31225                 x : x + (this.unitWidth + this.gutter) * 1,
31226                 y : y
31227             });
31228             
31229             return pos;
31230             
31231         }
31232         
31233         pos.push({
31234             x : x,
31235             y : y
31236         });
31237
31238         pos.push({
31239             x : x + (this.unitWidth + this.gutter) * 2,
31240             y : y
31241         });
31242
31243         pos.push({
31244             x : x + (this.unitWidth + this.gutter) * 2,
31245             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31246         });
31247             
31248         return pos;
31249         
31250     },
31251     
31252     getVerticalFourBoxColPositions : function(x, y, box)
31253     {
31254         var pos = [];
31255         
31256         if(box[0].size == 'xs'){
31257             
31258             pos.push({
31259                 x : x,
31260                 y : y
31261             });
31262
31263             pos.push({
31264                 x : x,
31265                 y : y + (this.unitHeight + this.gutter) * 1
31266             });
31267             
31268             pos.push({
31269                 x : x,
31270                 y : y + (this.unitHeight + this.gutter) * 2
31271             });
31272             
31273             pos.push({
31274                 x : x + (this.unitWidth + this.gutter) * 1,
31275                 y : y
31276             });
31277             
31278             return pos;
31279             
31280         }
31281         
31282         pos.push({
31283             x : x,
31284             y : y
31285         });
31286
31287         pos.push({
31288             x : x + (this.unitWidth + this.gutter) * 2,
31289             y : y
31290         });
31291
31292         pos.push({
31293             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31294             y : y + (this.unitHeight + this.gutter) * 1
31295         });
31296
31297         pos.push({
31298             x : x + (this.unitWidth + this.gutter) * 2,
31299             y : y + (this.unitWidth + this.gutter) * 2
31300         });
31301
31302         return pos;
31303         
31304     },
31305     
31306     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31307     {
31308         var pos = [];
31309         
31310         if(box[0].size == 'md-left'){
31311             pos.push({
31312                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31313                 y : minY
31314             });
31315             
31316             return pos;
31317         }
31318         
31319         if(box[0].size == 'md-right'){
31320             pos.push({
31321                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31322                 y : minY + (this.unitWidth + this.gutter) * 1
31323             });
31324             
31325             return pos;
31326         }
31327         
31328         var rand = Math.floor(Math.random() * (4 - box[0].y));
31329         
31330         pos.push({
31331             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31332             y : minY + (this.unitWidth + this.gutter) * rand
31333         });
31334         
31335         return pos;
31336         
31337     },
31338     
31339     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31340     {
31341         var pos = [];
31342         
31343         if(box[0].size == 'xs'){
31344             
31345             pos.push({
31346                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31347                 y : minY
31348             });
31349
31350             pos.push({
31351                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31352                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31353             });
31354             
31355             return pos;
31356             
31357         }
31358         
31359         pos.push({
31360             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31361             y : minY
31362         });
31363
31364         pos.push({
31365             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31366             y : minY + (this.unitWidth + this.gutter) * 2
31367         });
31368         
31369         return pos;
31370         
31371     },
31372     
31373     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31374     {
31375         var pos = [];
31376         
31377         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31378             
31379             pos.push({
31380                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31381                 y : minY
31382             });
31383
31384             pos.push({
31385                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31386                 y : minY + (this.unitWidth + this.gutter) * 1
31387             });
31388             
31389             pos.push({
31390                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31391                 y : minY + (this.unitWidth + this.gutter) * 2
31392             });
31393             
31394             return pos;
31395             
31396         }
31397         
31398         if(box[0].size == 'xs' && box[1].size == 'xs'){
31399             
31400             pos.push({
31401                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31402                 y : minY
31403             });
31404
31405             pos.push({
31406                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31407                 y : minY
31408             });
31409             
31410             pos.push({
31411                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31412                 y : minY + (this.unitWidth + this.gutter) * 1
31413             });
31414             
31415             return pos;
31416             
31417         }
31418         
31419         pos.push({
31420             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31421             y : minY
31422         });
31423
31424         pos.push({
31425             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31426             y : minY + (this.unitWidth + this.gutter) * 2
31427         });
31428
31429         pos.push({
31430             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31431             y : minY + (this.unitWidth + this.gutter) * 2
31432         });
31433             
31434         return pos;
31435         
31436     },
31437     
31438     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31439     {
31440         var pos = [];
31441         
31442         if(box[0].size == 'xs'){
31443             
31444             pos.push({
31445                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31446                 y : minY
31447             });
31448
31449             pos.push({
31450                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31451                 y : minY
31452             });
31453             
31454             pos.push({
31455                 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),
31456                 y : minY
31457             });
31458             
31459             pos.push({
31460                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31461                 y : minY + (this.unitWidth + this.gutter) * 1
31462             });
31463             
31464             return pos;
31465             
31466         }
31467         
31468         pos.push({
31469             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31470             y : minY
31471         });
31472         
31473         pos.push({
31474             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31475             y : minY + (this.unitWidth + this.gutter) * 2
31476         });
31477         
31478         pos.push({
31479             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31480             y : minY + (this.unitWidth + this.gutter) * 2
31481         });
31482         
31483         pos.push({
31484             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),
31485             y : minY + (this.unitWidth + this.gutter) * 2
31486         });
31487
31488         return pos;
31489         
31490     },
31491     
31492     /**
31493     * remove a Masonry Brick
31494     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31495     */
31496     removeBrick : function(brick_id)
31497     {
31498         if (!brick_id) {
31499             return;
31500         }
31501         
31502         for (var i = 0; i<this.bricks.length; i++) {
31503             if (this.bricks[i].id == brick_id) {
31504                 this.bricks.splice(i,1);
31505                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31506                 this.initial();
31507             }
31508         }
31509     },
31510     
31511     /**
31512     * adds a Masonry Brick
31513     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31514     */
31515     addBrick : function(cfg)
31516     {
31517         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31518         //this.register(cn);
31519         cn.parentId = this.id;
31520         cn.onRender(this.el, null);
31521         return cn;
31522     },
31523     
31524     /**
31525     * register a Masonry Brick
31526     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31527     */
31528     
31529     register : function(brick)
31530     {
31531         this.bricks.push(brick);
31532         brick.masonryId = this.id;
31533     },
31534     
31535     /**
31536     * clear all the Masonry Brick
31537     */
31538     clearAll : function()
31539     {
31540         this.bricks = [];
31541         //this.getChildContainer().dom.innerHTML = "";
31542         this.el.dom.innerHTML = '';
31543     },
31544     
31545     getSelected : function()
31546     {
31547         if (!this.selectedBrick) {
31548             return false;
31549         }
31550         
31551         return this.selectedBrick;
31552     }
31553 });
31554
31555 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31556     
31557     groups: {},
31558      /**
31559     * register a Masonry Layout
31560     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31561     */
31562     
31563     register : function(layout)
31564     {
31565         this.groups[layout.id] = layout;
31566     },
31567     /**
31568     * fetch a  Masonry Layout based on the masonry layout ID
31569     * @param {string} the masonry layout to add
31570     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31571     */
31572     
31573     get: function(layout_id) {
31574         if (typeof(this.groups[layout_id]) == 'undefined') {
31575             return false;
31576         }
31577         return this.groups[layout_id] ;
31578     }
31579     
31580     
31581     
31582 });
31583
31584  
31585
31586  /**
31587  *
31588  * This is based on 
31589  * http://masonry.desandro.com
31590  *
31591  * The idea is to render all the bricks based on vertical width...
31592  *
31593  * The original code extends 'outlayer' - we might need to use that....
31594  * 
31595  */
31596
31597
31598 /**
31599  * @class Roo.bootstrap.LayoutMasonryAuto
31600  * @extends Roo.bootstrap.Component
31601  * Bootstrap Layout Masonry class
31602  * 
31603  * @constructor
31604  * Create a new Element
31605  * @param {Object} config The config object
31606  */
31607
31608 Roo.bootstrap.LayoutMasonryAuto = function(config){
31609     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31610 };
31611
31612 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31613     
31614       /**
31615      * @cfg {Boolean} isFitWidth  - resize the width..
31616      */   
31617     isFitWidth : false,  // options..
31618     /**
31619      * @cfg {Boolean} isOriginLeft = left align?
31620      */   
31621     isOriginLeft : true,
31622     /**
31623      * @cfg {Boolean} isOriginTop = top align?
31624      */   
31625     isOriginTop : false,
31626     /**
31627      * @cfg {Boolean} isLayoutInstant = no animation?
31628      */   
31629     isLayoutInstant : false, // needed?
31630     /**
31631      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31632      */   
31633     isResizingContainer : true,
31634     /**
31635      * @cfg {Number} columnWidth  width of the columns 
31636      */   
31637     
31638     columnWidth : 0,
31639     
31640     /**
31641      * @cfg {Number} maxCols maximum number of columns
31642      */   
31643     
31644     maxCols: 0,
31645     /**
31646      * @cfg {Number} padHeight padding below box..
31647      */   
31648     
31649     padHeight : 10, 
31650     
31651     /**
31652      * @cfg {Boolean} isAutoInitial defalut true
31653      */   
31654     
31655     isAutoInitial : true, 
31656     
31657     // private?
31658     gutter : 0,
31659     
31660     containerWidth: 0,
31661     initialColumnWidth : 0,
31662     currentSize : null,
31663     
31664     colYs : null, // array.
31665     maxY : 0,
31666     padWidth: 10,
31667     
31668     
31669     tag: 'div',
31670     cls: '',
31671     bricks: null, //CompositeElement
31672     cols : 0, // array?
31673     // element : null, // wrapped now this.el
31674     _isLayoutInited : null, 
31675     
31676     
31677     getAutoCreate : function(){
31678         
31679         var cfg = {
31680             tag: this.tag,
31681             cls: 'blog-masonary-wrapper ' + this.cls,
31682             cn : {
31683                 cls : 'mas-boxes masonary'
31684             }
31685         };
31686         
31687         return cfg;
31688     },
31689     
31690     getChildContainer: function( )
31691     {
31692         if (this.boxesEl) {
31693             return this.boxesEl;
31694         }
31695         
31696         this.boxesEl = this.el.select('.mas-boxes').first();
31697         
31698         return this.boxesEl;
31699     },
31700     
31701     
31702     initEvents : function()
31703     {
31704         var _this = this;
31705         
31706         if(this.isAutoInitial){
31707             Roo.log('hook children rendered');
31708             this.on('childrenrendered', function() {
31709                 Roo.log('children rendered');
31710                 _this.initial();
31711             } ,this);
31712         }
31713         
31714     },
31715     
31716     initial : function()
31717     {
31718         this.reloadItems();
31719
31720         this.currentSize = this.el.getBox(true);
31721
31722         /// was window resize... - let's see if this works..
31723         Roo.EventManager.onWindowResize(this.resize, this); 
31724
31725         if(!this.isAutoInitial){
31726             this.layout();
31727             return;
31728         }
31729         
31730         this.layout.defer(500,this);
31731     },
31732     
31733     reloadItems: function()
31734     {
31735         this.bricks = this.el.select('.masonry-brick', true);
31736         
31737         this.bricks.each(function(b) {
31738             //Roo.log(b.getSize());
31739             if (!b.attr('originalwidth')) {
31740                 b.attr('originalwidth',  b.getSize().width);
31741             }
31742             
31743         });
31744         
31745         Roo.log(this.bricks.elements.length);
31746     },
31747     
31748     resize : function()
31749     {
31750         Roo.log('resize');
31751         var cs = this.el.getBox(true);
31752         
31753         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31754             Roo.log("no change in with or X");
31755             return;
31756         }
31757         this.currentSize = cs;
31758         this.layout();
31759     },
31760     
31761     layout : function()
31762     {
31763          Roo.log('layout');
31764         this._resetLayout();
31765         //this._manageStamps();
31766       
31767         // don't animate first layout
31768         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31769         this.layoutItems( isInstant );
31770       
31771         // flag for initalized
31772         this._isLayoutInited = true;
31773     },
31774     
31775     layoutItems : function( isInstant )
31776     {
31777         //var items = this._getItemsForLayout( this.items );
31778         // original code supports filtering layout items.. we just ignore it..
31779         
31780         this._layoutItems( this.bricks , isInstant );
31781       
31782         this._postLayout();
31783     },
31784     _layoutItems : function ( items , isInstant)
31785     {
31786        //this.fireEvent( 'layout', this, items );
31787     
31788
31789         if ( !items || !items.elements.length ) {
31790           // no items, emit event with empty array
31791             return;
31792         }
31793
31794         var queue = [];
31795         items.each(function(item) {
31796             Roo.log("layout item");
31797             Roo.log(item);
31798             // get x/y object from method
31799             var position = this._getItemLayoutPosition( item );
31800             // enqueue
31801             position.item = item;
31802             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31803             queue.push( position );
31804         }, this);
31805       
31806         this._processLayoutQueue( queue );
31807     },
31808     /** Sets position of item in DOM
31809     * @param {Element} item
31810     * @param {Number} x - horizontal position
31811     * @param {Number} y - vertical position
31812     * @param {Boolean} isInstant - disables transitions
31813     */
31814     _processLayoutQueue : function( queue )
31815     {
31816         for ( var i=0, len = queue.length; i < len; i++ ) {
31817             var obj = queue[i];
31818             obj.item.position('absolute');
31819             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31820         }
31821     },
31822       
31823     
31824     /**
31825     * Any logic you want to do after each layout,
31826     * i.e. size the container
31827     */
31828     _postLayout : function()
31829     {
31830         this.resizeContainer();
31831     },
31832     
31833     resizeContainer : function()
31834     {
31835         if ( !this.isResizingContainer ) {
31836             return;
31837         }
31838         var size = this._getContainerSize();
31839         if ( size ) {
31840             this.el.setSize(size.width,size.height);
31841             this.boxesEl.setSize(size.width,size.height);
31842         }
31843     },
31844     
31845     
31846     
31847     _resetLayout : function()
31848     {
31849         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31850         this.colWidth = this.el.getWidth();
31851         //this.gutter = this.el.getWidth(); 
31852         
31853         this.measureColumns();
31854
31855         // reset column Y
31856         var i = this.cols;
31857         this.colYs = [];
31858         while (i--) {
31859             this.colYs.push( 0 );
31860         }
31861     
31862         this.maxY = 0;
31863     },
31864
31865     measureColumns : function()
31866     {
31867         this.getContainerWidth();
31868       // if columnWidth is 0, default to outerWidth of first item
31869         if ( !this.columnWidth ) {
31870             var firstItem = this.bricks.first();
31871             Roo.log(firstItem);
31872             this.columnWidth  = this.containerWidth;
31873             if (firstItem && firstItem.attr('originalwidth') ) {
31874                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31875             }
31876             // columnWidth fall back to item of first element
31877             Roo.log("set column width?");
31878                         this.initialColumnWidth = this.columnWidth  ;
31879
31880             // if first elem has no width, default to size of container
31881             
31882         }
31883         
31884         
31885         if (this.initialColumnWidth) {
31886             this.columnWidth = this.initialColumnWidth;
31887         }
31888         
31889         
31890             
31891         // column width is fixed at the top - however if container width get's smaller we should
31892         // reduce it...
31893         
31894         // this bit calcs how man columns..
31895             
31896         var columnWidth = this.columnWidth += this.gutter;
31897       
31898         // calculate columns
31899         var containerWidth = this.containerWidth + this.gutter;
31900         
31901         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31902         // fix rounding errors, typically with gutters
31903         var excess = columnWidth - containerWidth % columnWidth;
31904         
31905         
31906         // if overshoot is less than a pixel, round up, otherwise floor it
31907         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31908         cols = Math[ mathMethod ]( cols );
31909         this.cols = Math.max( cols, 1 );
31910         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31911         
31912          // padding positioning..
31913         var totalColWidth = this.cols * this.columnWidth;
31914         var padavail = this.containerWidth - totalColWidth;
31915         // so for 2 columns - we need 3 'pads'
31916         
31917         var padNeeded = (1+this.cols) * this.padWidth;
31918         
31919         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31920         
31921         this.columnWidth += padExtra
31922         //this.padWidth = Math.floor(padavail /  ( this.cols));
31923         
31924         // adjust colum width so that padding is fixed??
31925         
31926         // we have 3 columns ... total = width * 3
31927         // we have X left over... that should be used by 
31928         
31929         //if (this.expandC) {
31930             
31931         //}
31932         
31933         
31934         
31935     },
31936     
31937     getContainerWidth : function()
31938     {
31939        /* // container is parent if fit width
31940         var container = this.isFitWidth ? this.element.parentNode : this.element;
31941         // check that this.size and size are there
31942         // IE8 triggers resize on body size change, so they might not be
31943         
31944         var size = getSize( container );  //FIXME
31945         this.containerWidth = size && size.innerWidth; //FIXME
31946         */
31947          
31948         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31949         
31950     },
31951     
31952     _getItemLayoutPosition : function( item )  // what is item?
31953     {
31954         // we resize the item to our columnWidth..
31955       
31956         item.setWidth(this.columnWidth);
31957         item.autoBoxAdjust  = false;
31958         
31959         var sz = item.getSize();
31960  
31961         // how many columns does this brick span
31962         var remainder = this.containerWidth % this.columnWidth;
31963         
31964         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31965         // round if off by 1 pixel, otherwise use ceil
31966         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31967         colSpan = Math.min( colSpan, this.cols );
31968         
31969         // normally this should be '1' as we dont' currently allow multi width columns..
31970         
31971         var colGroup = this._getColGroup( colSpan );
31972         // get the minimum Y value from the columns
31973         var minimumY = Math.min.apply( Math, colGroup );
31974         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31975         
31976         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31977          
31978         // position the brick
31979         var position = {
31980             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31981             y: this.currentSize.y + minimumY + this.padHeight
31982         };
31983         
31984         Roo.log(position);
31985         // apply setHeight to necessary columns
31986         var setHeight = minimumY + sz.height + this.padHeight;
31987         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31988         
31989         var setSpan = this.cols + 1 - colGroup.length;
31990         for ( var i = 0; i < setSpan; i++ ) {
31991           this.colYs[ shortColIndex + i ] = setHeight ;
31992         }
31993       
31994         return position;
31995     },
31996     
31997     /**
31998      * @param {Number} colSpan - number of columns the element spans
31999      * @returns {Array} colGroup
32000      */
32001     _getColGroup : function( colSpan )
32002     {
32003         if ( colSpan < 2 ) {
32004           // if brick spans only one column, use all the column Ys
32005           return this.colYs;
32006         }
32007       
32008         var colGroup = [];
32009         // how many different places could this brick fit horizontally
32010         var groupCount = this.cols + 1 - colSpan;
32011         // for each group potential horizontal position
32012         for ( var i = 0; i < groupCount; i++ ) {
32013           // make an array of colY values for that one group
32014           var groupColYs = this.colYs.slice( i, i + colSpan );
32015           // and get the max value of the array
32016           colGroup[i] = Math.max.apply( Math, groupColYs );
32017         }
32018         return colGroup;
32019     },
32020     /*
32021     _manageStamp : function( stamp )
32022     {
32023         var stampSize =  stamp.getSize();
32024         var offset = stamp.getBox();
32025         // get the columns that this stamp affects
32026         var firstX = this.isOriginLeft ? offset.x : offset.right;
32027         var lastX = firstX + stampSize.width;
32028         var firstCol = Math.floor( firstX / this.columnWidth );
32029         firstCol = Math.max( 0, firstCol );
32030         
32031         var lastCol = Math.floor( lastX / this.columnWidth );
32032         // lastCol should not go over if multiple of columnWidth #425
32033         lastCol -= lastX % this.columnWidth ? 0 : 1;
32034         lastCol = Math.min( this.cols - 1, lastCol );
32035         
32036         // set colYs to bottom of the stamp
32037         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32038             stampSize.height;
32039             
32040         for ( var i = firstCol; i <= lastCol; i++ ) {
32041           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32042         }
32043     },
32044     */
32045     
32046     _getContainerSize : function()
32047     {
32048         this.maxY = Math.max.apply( Math, this.colYs );
32049         var size = {
32050             height: this.maxY
32051         };
32052       
32053         if ( this.isFitWidth ) {
32054             size.width = this._getContainerFitWidth();
32055         }
32056       
32057         return size;
32058     },
32059     
32060     _getContainerFitWidth : function()
32061     {
32062         var unusedCols = 0;
32063         // count unused columns
32064         var i = this.cols;
32065         while ( --i ) {
32066           if ( this.colYs[i] !== 0 ) {
32067             break;
32068           }
32069           unusedCols++;
32070         }
32071         // fit container to columns that have been used
32072         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32073     },
32074     
32075     needsResizeLayout : function()
32076     {
32077         var previousWidth = this.containerWidth;
32078         this.getContainerWidth();
32079         return previousWidth !== this.containerWidth;
32080     }
32081  
32082 });
32083
32084  
32085
32086  /*
32087  * - LGPL
32088  *
32089  * element
32090  * 
32091  */
32092
32093 /**
32094  * @class Roo.bootstrap.MasonryBrick
32095  * @extends Roo.bootstrap.Component
32096  * Bootstrap MasonryBrick class
32097  * 
32098  * @constructor
32099  * Create a new MasonryBrick
32100  * @param {Object} config The config object
32101  */
32102
32103 Roo.bootstrap.MasonryBrick = function(config){
32104     
32105     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32106     
32107     Roo.bootstrap.MasonryBrick.register(this);
32108     
32109     this.addEvents({
32110         // raw events
32111         /**
32112          * @event click
32113          * When a MasonryBrick is clcik
32114          * @param {Roo.bootstrap.MasonryBrick} this
32115          * @param {Roo.EventObject} e
32116          */
32117         "click" : true
32118     });
32119 };
32120
32121 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32122     
32123     /**
32124      * @cfg {String} title
32125      */   
32126     title : '',
32127     /**
32128      * @cfg {String} html
32129      */   
32130     html : '',
32131     /**
32132      * @cfg {String} bgimage
32133      */   
32134     bgimage : '',
32135     /**
32136      * @cfg {String} videourl
32137      */   
32138     videourl : '',
32139     /**
32140      * @cfg {String} cls
32141      */   
32142     cls : '',
32143     /**
32144      * @cfg {String} href
32145      */   
32146     href : '',
32147     /**
32148      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32149      */   
32150     size : 'xs',
32151     
32152     /**
32153      * @cfg {String} placetitle (center|bottom)
32154      */   
32155     placetitle : '',
32156     
32157     /**
32158      * @cfg {Boolean} isFitContainer defalut true
32159      */   
32160     isFitContainer : true, 
32161     
32162     /**
32163      * @cfg {Boolean} preventDefault defalut false
32164      */   
32165     preventDefault : false, 
32166     
32167     /**
32168      * @cfg {Boolean} inverse defalut false
32169      */   
32170     maskInverse : false, 
32171     
32172     getAutoCreate : function()
32173     {
32174         if(!this.isFitContainer){
32175             return this.getSplitAutoCreate();
32176         }
32177         
32178         var cls = 'masonry-brick masonry-brick-full';
32179         
32180         if(this.href.length){
32181             cls += ' masonry-brick-link';
32182         }
32183         
32184         if(this.bgimage.length){
32185             cls += ' masonry-brick-image';
32186         }
32187         
32188         if(this.maskInverse){
32189             cls += ' mask-inverse';
32190         }
32191         
32192         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32193             cls += ' enable-mask';
32194         }
32195         
32196         if(this.size){
32197             cls += ' masonry-' + this.size + '-brick';
32198         }
32199         
32200         if(this.placetitle.length){
32201             
32202             switch (this.placetitle) {
32203                 case 'center' :
32204                     cls += ' masonry-center-title';
32205                     break;
32206                 case 'bottom' :
32207                     cls += ' masonry-bottom-title';
32208                     break;
32209                 default:
32210                     break;
32211             }
32212             
32213         } else {
32214             if(!this.html.length && !this.bgimage.length){
32215                 cls += ' masonry-center-title';
32216             }
32217
32218             if(!this.html.length && this.bgimage.length){
32219                 cls += ' masonry-bottom-title';
32220             }
32221         }
32222         
32223         if(this.cls){
32224             cls += ' ' + this.cls;
32225         }
32226         
32227         var cfg = {
32228             tag: (this.href.length) ? 'a' : 'div',
32229             cls: cls,
32230             cn: [
32231                 {
32232                     tag: 'div',
32233                     cls: 'masonry-brick-mask'
32234                 },
32235                 {
32236                     tag: 'div',
32237                     cls: 'masonry-brick-paragraph',
32238                     cn: []
32239                 }
32240             ]
32241         };
32242         
32243         if(this.href.length){
32244             cfg.href = this.href;
32245         }
32246         
32247         var cn = cfg.cn[1].cn;
32248         
32249         if(this.title.length){
32250             cn.push({
32251                 tag: 'h4',
32252                 cls: 'masonry-brick-title',
32253                 html: this.title
32254             });
32255         }
32256         
32257         if(this.html.length){
32258             cn.push({
32259                 tag: 'p',
32260                 cls: 'masonry-brick-text',
32261                 html: this.html
32262             });
32263         }
32264         
32265         if (!this.title.length && !this.html.length) {
32266             cfg.cn[1].cls += ' hide';
32267         }
32268         
32269         if(this.bgimage.length){
32270             cfg.cn.push({
32271                 tag: 'img',
32272                 cls: 'masonry-brick-image-view',
32273                 src: this.bgimage
32274             });
32275         }
32276         
32277         if(this.videourl.length){
32278             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32279             // youtube support only?
32280             cfg.cn.push({
32281                 tag: 'iframe',
32282                 cls: 'masonry-brick-image-view',
32283                 src: vurl,
32284                 frameborder : 0,
32285                 allowfullscreen : true
32286             });
32287         }
32288         
32289         return cfg;
32290         
32291     },
32292     
32293     getSplitAutoCreate : function()
32294     {
32295         var cls = 'masonry-brick masonry-brick-split';
32296         
32297         if(this.href.length){
32298             cls += ' masonry-brick-link';
32299         }
32300         
32301         if(this.bgimage.length){
32302             cls += ' masonry-brick-image';
32303         }
32304         
32305         if(this.size){
32306             cls += ' masonry-' + this.size + '-brick';
32307         }
32308         
32309         switch (this.placetitle) {
32310             case 'center' :
32311                 cls += ' masonry-center-title';
32312                 break;
32313             case 'bottom' :
32314                 cls += ' masonry-bottom-title';
32315                 break;
32316             default:
32317                 if(!this.bgimage.length){
32318                     cls += ' masonry-center-title';
32319                 }
32320
32321                 if(this.bgimage.length){
32322                     cls += ' masonry-bottom-title';
32323                 }
32324                 break;
32325         }
32326         
32327         if(this.cls){
32328             cls += ' ' + this.cls;
32329         }
32330         
32331         var cfg = {
32332             tag: (this.href.length) ? 'a' : 'div',
32333             cls: cls,
32334             cn: [
32335                 {
32336                     tag: 'div',
32337                     cls: 'masonry-brick-split-head',
32338                     cn: [
32339                         {
32340                             tag: 'div',
32341                             cls: 'masonry-brick-paragraph',
32342                             cn: []
32343                         }
32344                     ]
32345                 },
32346                 {
32347                     tag: 'div',
32348                     cls: 'masonry-brick-split-body',
32349                     cn: []
32350                 }
32351             ]
32352         };
32353         
32354         if(this.href.length){
32355             cfg.href = this.href;
32356         }
32357         
32358         if(this.title.length){
32359             cfg.cn[0].cn[0].cn.push({
32360                 tag: 'h4',
32361                 cls: 'masonry-brick-title',
32362                 html: this.title
32363             });
32364         }
32365         
32366         if(this.html.length){
32367             cfg.cn[1].cn.push({
32368                 tag: 'p',
32369                 cls: 'masonry-brick-text',
32370                 html: this.html
32371             });
32372         }
32373
32374         if(this.bgimage.length){
32375             cfg.cn[0].cn.push({
32376                 tag: 'img',
32377                 cls: 'masonry-brick-image-view',
32378                 src: this.bgimage
32379             });
32380         }
32381         
32382         if(this.videourl.length){
32383             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32384             // youtube support only?
32385             cfg.cn[0].cn.cn.push({
32386                 tag: 'iframe',
32387                 cls: 'masonry-brick-image-view',
32388                 src: vurl,
32389                 frameborder : 0,
32390                 allowfullscreen : true
32391             });
32392         }
32393         
32394         return cfg;
32395     },
32396     
32397     initEvents: function() 
32398     {
32399         switch (this.size) {
32400             case 'xs' :
32401                 this.x = 1;
32402                 this.y = 1;
32403                 break;
32404             case 'sm' :
32405                 this.x = 2;
32406                 this.y = 2;
32407                 break;
32408             case 'md' :
32409             case 'md-left' :
32410             case 'md-right' :
32411                 this.x = 3;
32412                 this.y = 3;
32413                 break;
32414             case 'tall' :
32415                 this.x = 2;
32416                 this.y = 3;
32417                 break;
32418             case 'wide' :
32419                 this.x = 3;
32420                 this.y = 2;
32421                 break;
32422             case 'wide-thin' :
32423                 this.x = 3;
32424                 this.y = 1;
32425                 break;
32426                         
32427             default :
32428                 break;
32429         }
32430         
32431         if(Roo.isTouch){
32432             this.el.on('touchstart', this.onTouchStart, this);
32433             this.el.on('touchmove', this.onTouchMove, this);
32434             this.el.on('touchend', this.onTouchEnd, this);
32435             this.el.on('contextmenu', this.onContextMenu, this);
32436         } else {
32437             this.el.on('mouseenter'  ,this.enter, this);
32438             this.el.on('mouseleave', this.leave, this);
32439             this.el.on('click', this.onClick, this);
32440         }
32441         
32442         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32443             this.parent().bricks.push(this);   
32444         }
32445         
32446     },
32447     
32448     onClick: function(e, el)
32449     {
32450         var time = this.endTimer - this.startTimer;
32451         // Roo.log(e.preventDefault());
32452         if(Roo.isTouch){
32453             if(time > 1000){
32454                 e.preventDefault();
32455                 return;
32456             }
32457         }
32458         
32459         if(!this.preventDefault){
32460             return;
32461         }
32462         
32463         e.preventDefault();
32464         
32465         if (this.activcClass != '') {
32466             this.selectBrick();
32467         }
32468         
32469         this.fireEvent('click', this);
32470     },
32471     
32472     enter: function(e, el)
32473     {
32474         e.preventDefault();
32475         
32476         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32477             return;
32478         }
32479         
32480         if(this.bgimage.length && this.html.length){
32481             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32482         }
32483     },
32484     
32485     leave: function(e, el)
32486     {
32487         e.preventDefault();
32488         
32489         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32490             return;
32491         }
32492         
32493         if(this.bgimage.length && this.html.length){
32494             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32495         }
32496     },
32497     
32498     onTouchStart: function(e, el)
32499     {
32500 //        e.preventDefault();
32501         
32502         this.touchmoved = false;
32503         
32504         if(!this.isFitContainer){
32505             return;
32506         }
32507         
32508         if(!this.bgimage.length || !this.html.length){
32509             return;
32510         }
32511         
32512         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32513         
32514         this.timer = new Date().getTime();
32515         
32516     },
32517     
32518     onTouchMove: function(e, el)
32519     {
32520         this.touchmoved = true;
32521     },
32522     
32523     onContextMenu : function(e,el)
32524     {
32525         e.preventDefault();
32526         e.stopPropagation();
32527         return false;
32528     },
32529     
32530     onTouchEnd: function(e, el)
32531     {
32532 //        e.preventDefault();
32533         
32534         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32535         
32536             this.leave(e,el);
32537             
32538             return;
32539         }
32540         
32541         if(!this.bgimage.length || !this.html.length){
32542             
32543             if(this.href.length){
32544                 window.location.href = this.href;
32545             }
32546             
32547             return;
32548         }
32549         
32550         if(!this.isFitContainer){
32551             return;
32552         }
32553         
32554         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32555         
32556         window.location.href = this.href;
32557     },
32558     
32559     //selection on single brick only
32560     selectBrick : function() {
32561         
32562         if (!this.parentId) {
32563             return;
32564         }
32565         
32566         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32567         var index = m.selectedBrick.indexOf(this.id);
32568         
32569         if ( index > -1) {
32570             m.selectedBrick.splice(index,1);
32571             this.el.removeClass(this.activeClass);
32572             return;
32573         }
32574         
32575         for(var i = 0; i < m.selectedBrick.length; i++) {
32576             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32577             b.el.removeClass(b.activeClass);
32578         }
32579         
32580         m.selectedBrick = [];
32581         
32582         m.selectedBrick.push(this.id);
32583         this.el.addClass(this.activeClass);
32584         return;
32585     }
32586     
32587 });
32588
32589 Roo.apply(Roo.bootstrap.MasonryBrick, {
32590     
32591     //groups: {},
32592     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32593      /**
32594     * register a Masonry Brick
32595     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32596     */
32597     
32598     register : function(brick)
32599     {
32600         //this.groups[brick.id] = brick;
32601         this.groups.add(brick.id, brick);
32602     },
32603     /**
32604     * fetch a  masonry brick based on the masonry brick ID
32605     * @param {string} the masonry brick to add
32606     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32607     */
32608     
32609     get: function(brick_id) 
32610     {
32611         // if (typeof(this.groups[brick_id]) == 'undefined') {
32612         //     return false;
32613         // }
32614         // return this.groups[brick_id] ;
32615         
32616         if(this.groups.key(brick_id)) {
32617             return this.groups.key(brick_id);
32618         }
32619         
32620         return false;
32621     }
32622     
32623     
32624     
32625 });
32626
32627  /*
32628  * - LGPL
32629  *
32630  * element
32631  * 
32632  */
32633
32634 /**
32635  * @class Roo.bootstrap.Brick
32636  * @extends Roo.bootstrap.Component
32637  * Bootstrap Brick class
32638  * 
32639  * @constructor
32640  * Create a new Brick
32641  * @param {Object} config The config object
32642  */
32643
32644 Roo.bootstrap.Brick = function(config){
32645     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32646     
32647     this.addEvents({
32648         // raw events
32649         /**
32650          * @event click
32651          * When a Brick is click
32652          * @param {Roo.bootstrap.Brick} this
32653          * @param {Roo.EventObject} e
32654          */
32655         "click" : true
32656     });
32657 };
32658
32659 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32660     
32661     /**
32662      * @cfg {String} title
32663      */   
32664     title : '',
32665     /**
32666      * @cfg {String} html
32667      */   
32668     html : '',
32669     /**
32670      * @cfg {String} bgimage
32671      */   
32672     bgimage : '',
32673     /**
32674      * @cfg {String} cls
32675      */   
32676     cls : '',
32677     /**
32678      * @cfg {String} href
32679      */   
32680     href : '',
32681     /**
32682      * @cfg {String} video
32683      */   
32684     video : '',
32685     /**
32686      * @cfg {Boolean} square
32687      */   
32688     square : true,
32689     
32690     getAutoCreate : function()
32691     {
32692         var cls = 'roo-brick';
32693         
32694         if(this.href.length){
32695             cls += ' roo-brick-link';
32696         }
32697         
32698         if(this.bgimage.length){
32699             cls += ' roo-brick-image';
32700         }
32701         
32702         if(!this.html.length && !this.bgimage.length){
32703             cls += ' roo-brick-center-title';
32704         }
32705         
32706         if(!this.html.length && this.bgimage.length){
32707             cls += ' roo-brick-bottom-title';
32708         }
32709         
32710         if(this.cls){
32711             cls += ' ' + this.cls;
32712         }
32713         
32714         var cfg = {
32715             tag: (this.href.length) ? 'a' : 'div',
32716             cls: cls,
32717             cn: [
32718                 {
32719                     tag: 'div',
32720                     cls: 'roo-brick-paragraph',
32721                     cn: []
32722                 }
32723             ]
32724         };
32725         
32726         if(this.href.length){
32727             cfg.href = this.href;
32728         }
32729         
32730         var cn = cfg.cn[0].cn;
32731         
32732         if(this.title.length){
32733             cn.push({
32734                 tag: 'h4',
32735                 cls: 'roo-brick-title',
32736                 html: this.title
32737             });
32738         }
32739         
32740         if(this.html.length){
32741             cn.push({
32742                 tag: 'p',
32743                 cls: 'roo-brick-text',
32744                 html: this.html
32745             });
32746         } else {
32747             cn.cls += ' hide';
32748         }
32749         
32750         if(this.bgimage.length){
32751             cfg.cn.push({
32752                 tag: 'img',
32753                 cls: 'roo-brick-image-view',
32754                 src: this.bgimage
32755             });
32756         }
32757         
32758         return cfg;
32759     },
32760     
32761     initEvents: function() 
32762     {
32763         if(this.title.length || this.html.length){
32764             this.el.on('mouseenter'  ,this.enter, this);
32765             this.el.on('mouseleave', this.leave, this);
32766         }
32767         
32768         Roo.EventManager.onWindowResize(this.resize, this); 
32769         
32770         if(this.bgimage.length){
32771             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32772             this.imageEl.on('load', this.onImageLoad, this);
32773             return;
32774         }
32775         
32776         this.resize();
32777     },
32778     
32779     onImageLoad : function()
32780     {
32781         this.resize();
32782     },
32783     
32784     resize : function()
32785     {
32786         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32787         
32788         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32789         
32790         if(this.bgimage.length){
32791             var image = this.el.select('.roo-brick-image-view', true).first();
32792             
32793             image.setWidth(paragraph.getWidth());
32794             
32795             if(this.square){
32796                 image.setHeight(paragraph.getWidth());
32797             }
32798             
32799             this.el.setHeight(image.getHeight());
32800             paragraph.setHeight(image.getHeight());
32801             
32802         }
32803         
32804     },
32805     
32806     enter: function(e, el)
32807     {
32808         e.preventDefault();
32809         
32810         if(this.bgimage.length){
32811             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32812             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32813         }
32814     },
32815     
32816     leave: function(e, el)
32817     {
32818         e.preventDefault();
32819         
32820         if(this.bgimage.length){
32821             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32822             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32823         }
32824     }
32825     
32826 });
32827
32828  
32829
32830  /*
32831  * - LGPL
32832  *
32833  * Input
32834  * 
32835  */
32836
32837 /**
32838  * @class Roo.bootstrap.NumberField
32839  * @extends Roo.bootstrap.Input
32840  * Bootstrap NumberField class
32841  * 
32842  * 
32843  * 
32844  * 
32845  * @constructor
32846  * Create a new NumberField
32847  * @param {Object} config The config object
32848  */
32849
32850 Roo.bootstrap.NumberField = function(config){
32851     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32852 };
32853
32854 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32855     
32856     /**
32857      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32858      */
32859     allowDecimals : true,
32860     /**
32861      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32862      */
32863     decimalSeparator : ".",
32864     /**
32865      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32866      */
32867     decimalPrecision : 2,
32868     /**
32869      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32870      */
32871     allowNegative : true,
32872     /**
32873      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32874      */
32875     minValue : Number.NEGATIVE_INFINITY,
32876     /**
32877      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32878      */
32879     maxValue : Number.MAX_VALUE,
32880     /**
32881      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32882      */
32883     minText : "The minimum value for this field is {0}",
32884     /**
32885      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32886      */
32887     maxText : "The maximum value for this field is {0}",
32888     /**
32889      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32890      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32891      */
32892     nanText : "{0} is not a valid number",
32893     /**
32894      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32895      */
32896     castInt : true,
32897
32898     // private
32899     initEvents : function()
32900     {   
32901         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32902         
32903         var allowed = "0123456789";
32904         
32905         if(this.allowDecimals){
32906             allowed += this.decimalSeparator;
32907         }
32908         
32909         if(this.allowNegative){
32910             allowed += "-";
32911         }
32912         
32913         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32914         
32915         var keyPress = function(e){
32916             
32917             var k = e.getKey();
32918             
32919             var c = e.getCharCode();
32920             
32921             if(
32922                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32923                     allowed.indexOf(String.fromCharCode(c)) === -1
32924             ){
32925                 e.stopEvent();
32926                 return;
32927             }
32928             
32929             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32930                 return;
32931             }
32932             
32933             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32934                 e.stopEvent();
32935             }
32936         };
32937         
32938         this.el.on("keypress", keyPress, this);
32939     },
32940     
32941     validateValue : function(value)
32942     {
32943         
32944         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32945             return false;
32946         }
32947         
32948         var num = this.parseValue(value);
32949         
32950         if(isNaN(num)){
32951             this.markInvalid(String.format(this.nanText, value));
32952             return false;
32953         }
32954         
32955         if(num < this.minValue){
32956             this.markInvalid(String.format(this.minText, this.minValue));
32957             return false;
32958         }
32959         
32960         if(num > this.maxValue){
32961             this.markInvalid(String.format(this.maxText, this.maxValue));
32962             return false;
32963         }
32964         
32965         return true;
32966     },
32967
32968     getValue : function()
32969     {
32970         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32971     },
32972
32973     parseValue : function(value)
32974     {
32975         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32976         return isNaN(value) ? '' : value;
32977     },
32978
32979     fixPrecision : function(value)
32980     {
32981         var nan = isNaN(value);
32982         
32983         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32984             return nan ? '' : value;
32985         }
32986         return parseFloat(value).toFixed(this.decimalPrecision);
32987     },
32988
32989     setValue : function(v)
32990     {
32991         v = this.fixPrecision(v);
32992         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32993     },
32994
32995     decimalPrecisionFcn : function(v)
32996     {
32997         return Math.floor(v);
32998     },
32999
33000     beforeBlur : function()
33001     {
33002         if(!this.castInt){
33003             return;
33004         }
33005         
33006         var v = this.parseValue(this.getRawValue());
33007         if(v){
33008             this.setValue(v);
33009         }
33010     }
33011     
33012 });
33013
33014  
33015
33016 /*
33017 * Licence: LGPL
33018 */
33019
33020 /**
33021  * @class Roo.bootstrap.DocumentSlider
33022  * @extends Roo.bootstrap.Component
33023  * Bootstrap DocumentSlider class
33024  * 
33025  * @constructor
33026  * Create a new DocumentViewer
33027  * @param {Object} config The config object
33028  */
33029
33030 Roo.bootstrap.DocumentSlider = function(config){
33031     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33032     
33033     this.files = [];
33034     
33035     this.addEvents({
33036         /**
33037          * @event initial
33038          * Fire after initEvent
33039          * @param {Roo.bootstrap.DocumentSlider} this
33040          */
33041         "initial" : true,
33042         /**
33043          * @event update
33044          * Fire after update
33045          * @param {Roo.bootstrap.DocumentSlider} this
33046          */
33047         "update" : true,
33048         /**
33049          * @event click
33050          * Fire after click
33051          * @param {Roo.bootstrap.DocumentSlider} this
33052          */
33053         "click" : true
33054     });
33055 };
33056
33057 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33058     
33059     files : false,
33060     
33061     indicator : 0,
33062     
33063     getAutoCreate : function()
33064     {
33065         var cfg = {
33066             tag : 'div',
33067             cls : 'roo-document-slider',
33068             cn : [
33069                 {
33070                     tag : 'div',
33071                     cls : 'roo-document-slider-header',
33072                     cn : [
33073                         {
33074                             tag : 'div',
33075                             cls : 'roo-document-slider-header-title'
33076                         }
33077                     ]
33078                 },
33079                 {
33080                     tag : 'div',
33081                     cls : 'roo-document-slider-body',
33082                     cn : [
33083                         {
33084                             tag : 'div',
33085                             cls : 'roo-document-slider-prev',
33086                             cn : [
33087                                 {
33088                                     tag : 'i',
33089                                     cls : 'fa fa-chevron-left'
33090                                 }
33091                             ]
33092                         },
33093                         {
33094                             tag : 'div',
33095                             cls : 'roo-document-slider-thumb',
33096                             cn : [
33097                                 {
33098                                     tag : 'img',
33099                                     cls : 'roo-document-slider-image'
33100                                 }
33101                             ]
33102                         },
33103                         {
33104                             tag : 'div',
33105                             cls : 'roo-document-slider-next',
33106                             cn : [
33107                                 {
33108                                     tag : 'i',
33109                                     cls : 'fa fa-chevron-right'
33110                                 }
33111                             ]
33112                         }
33113                     ]
33114                 }
33115             ]
33116         };
33117         
33118         return cfg;
33119     },
33120     
33121     initEvents : function()
33122     {
33123         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33124         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33125         
33126         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33127         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33128         
33129         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33130         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33131         
33132         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33133         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33134         
33135         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33136         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33137         
33138         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33139         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33140         
33141         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33142         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33143         
33144         this.thumbEl.on('click', this.onClick, this);
33145         
33146         this.prevIndicator.on('click', this.prev, this);
33147         
33148         this.nextIndicator.on('click', this.next, this);
33149         
33150     },
33151     
33152     initial : function()
33153     {
33154         if(this.files.length){
33155             this.indicator = 1;
33156             this.update()
33157         }
33158         
33159         this.fireEvent('initial', this);
33160     },
33161     
33162     update : function()
33163     {
33164         this.imageEl.attr('src', this.files[this.indicator - 1]);
33165         
33166         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33167         
33168         this.prevIndicator.show();
33169         
33170         if(this.indicator == 1){
33171             this.prevIndicator.hide();
33172         }
33173         
33174         this.nextIndicator.show();
33175         
33176         if(this.indicator == this.files.length){
33177             this.nextIndicator.hide();
33178         }
33179         
33180         this.thumbEl.scrollTo('top');
33181         
33182         this.fireEvent('update', this);
33183     },
33184     
33185     onClick : function(e)
33186     {
33187         e.preventDefault();
33188         
33189         this.fireEvent('click', this);
33190     },
33191     
33192     prev : function(e)
33193     {
33194         e.preventDefault();
33195         
33196         this.indicator = Math.max(1, this.indicator - 1);
33197         
33198         this.update();
33199     },
33200     
33201     next : function(e)
33202     {
33203         e.preventDefault();
33204         
33205         this.indicator = Math.min(this.files.length, this.indicator + 1);
33206         
33207         this.update();
33208     }
33209 });
33210 /*
33211  * - LGPL
33212  *
33213  * RadioSet
33214  *
33215  *
33216  */
33217
33218 /**
33219  * @class Roo.bootstrap.RadioSet
33220  * @extends Roo.bootstrap.Input
33221  * Bootstrap RadioSet class
33222  * @cfg {String} indicatorpos (left|right) default left
33223  * @cfg {Boolean} inline (true|false) inline the element (default true)
33224  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33225  * @constructor
33226  * Create a new RadioSet
33227  * @param {Object} config The config object
33228  */
33229
33230 Roo.bootstrap.RadioSet = function(config){
33231     
33232     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33233     
33234     this.radioes = [];
33235     
33236     Roo.bootstrap.RadioSet.register(this);
33237     
33238     this.addEvents({
33239         /**
33240         * @event check
33241         * Fires when the element is checked or unchecked.
33242         * @param {Roo.bootstrap.RadioSet} this This radio
33243         * @param {Roo.bootstrap.Radio} item The checked item
33244         */
33245        check : true
33246     });
33247     
33248 };
33249
33250 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33251
33252     radioes : false,
33253     
33254     inline : true,
33255     
33256     weight : '',
33257     
33258     indicatorpos : 'left',
33259     
33260     getAutoCreate : function()
33261     {
33262         var label = {
33263             tag : 'label',
33264             cls : 'roo-radio-set-label',
33265             cn : [
33266                 {
33267                     tag : 'span',
33268                     html : this.fieldLabel
33269                 }
33270             ]
33271         };
33272         
33273         if(this.indicatorpos == 'left'){
33274             label.cn.unshift({
33275                 tag : 'i',
33276                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33277                 tooltip : 'This field is required'
33278             });
33279         } else {
33280             label.cn.push({
33281                 tag : 'i',
33282                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33283                 tooltip : 'This field is required'
33284             });
33285         }
33286         
33287         var items = {
33288             tag : 'div',
33289             cls : 'roo-radio-set-items'
33290         };
33291         
33292         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33293         
33294         if (align === 'left' && this.fieldLabel.length) {
33295             
33296             items = {
33297                 cls : "roo-radio-set-right", 
33298                 cn: [
33299                     items
33300                 ]
33301             };
33302             
33303             if(this.labelWidth > 12){
33304                 label.style = "width: " + this.labelWidth + 'px';
33305             }
33306             
33307             if(this.labelWidth < 13 && this.labelmd == 0){
33308                 this.labelmd = this.labelWidth;
33309             }
33310             
33311             if(this.labellg > 0){
33312                 label.cls += ' col-lg-' + this.labellg;
33313                 items.cls += ' col-lg-' + (12 - this.labellg);
33314             }
33315             
33316             if(this.labelmd > 0){
33317                 label.cls += ' col-md-' + this.labelmd;
33318                 items.cls += ' col-md-' + (12 - this.labelmd);
33319             }
33320             
33321             if(this.labelsm > 0){
33322                 label.cls += ' col-sm-' + this.labelsm;
33323                 items.cls += ' col-sm-' + (12 - this.labelsm);
33324             }
33325             
33326             if(this.labelxs > 0){
33327                 label.cls += ' col-xs-' + this.labelxs;
33328                 items.cls += ' col-xs-' + (12 - this.labelxs);
33329             }
33330         }
33331         
33332         var cfg = {
33333             tag : 'div',
33334             cls : 'roo-radio-set',
33335             cn : [
33336                 {
33337                     tag : 'input',
33338                     cls : 'roo-radio-set-input',
33339                     type : 'hidden',
33340                     name : this.name,
33341                     value : this.value ? this.value :  ''
33342                 },
33343                 label,
33344                 items
33345             ]
33346         };
33347         
33348         if(this.weight.length){
33349             cfg.cls += ' roo-radio-' + this.weight;
33350         }
33351         
33352         if(this.inline) {
33353             cfg.cls += ' roo-radio-set-inline';
33354         }
33355         
33356         var settings=this;
33357         ['xs','sm','md','lg'].map(function(size){
33358             if (settings[size]) {
33359                 cfg.cls += ' col-' + size + '-' + settings[size];
33360             }
33361         });
33362         
33363         return cfg;
33364         
33365     },
33366
33367     initEvents : function()
33368     {
33369         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33370         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33371         
33372         if(!this.fieldLabel.length){
33373             this.labelEl.hide();
33374         }
33375         
33376         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33377         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33378         
33379         this.indicatorEl().addClass('invisible');
33380         
33381         this.originalValue = this.getValue();
33382         
33383     },
33384     
33385     inputEl: function ()
33386     {
33387         return this.el.select('.roo-radio-set-input', true).first();
33388     },
33389     
33390     getChildContainer : function()
33391     {
33392         return this.itemsEl;
33393     },
33394     
33395     register : function(item)
33396     {
33397         this.radioes.push(item);
33398         
33399     },
33400     
33401     validate : function()
33402     {   
33403         var valid = false;
33404         
33405         Roo.each(this.radioes, function(i){
33406             if(!i.checked){
33407                 return;
33408             }
33409             
33410             valid = true;
33411             return false;
33412         });
33413         
33414         if(this.allowBlank) {
33415             return true;
33416         }
33417         
33418         if(this.disabled || valid){
33419             this.markValid();
33420             return true;
33421         }
33422         
33423         this.markInvalid();
33424         return false;
33425         
33426     },
33427     
33428     markValid : function()
33429     {
33430         if(this.labelEl.isVisible(true)){
33431             this.indicatorEl().removeClass('visible');
33432             this.indicatorEl().addClass('invisible');
33433         }
33434         
33435         this.el.removeClass([this.invalidClass, this.validClass]);
33436         this.el.addClass(this.validClass);
33437         
33438         this.fireEvent('valid', this);
33439     },
33440     
33441     markInvalid : function(msg)
33442     {
33443         if(this.allowBlank || this.disabled){
33444             return;
33445         }
33446         
33447         if(this.labelEl.isVisible(true)){
33448             this.indicatorEl().removeClass('invisible');
33449             this.indicatorEl().addClass('visible');
33450         }
33451         
33452         this.el.removeClass([this.invalidClass, this.validClass]);
33453         this.el.addClass(this.invalidClass);
33454         
33455         this.fireEvent('invalid', this, msg);
33456         
33457     },
33458     
33459     setValue : function(v, suppressEvent)
33460     {   
33461         this.value = v;
33462         if(this.rendered){
33463             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33464         }
33465         
33466         Roo.each(this.radioes, function(i){
33467             
33468             i.checked = false;
33469             i.el.removeClass('checked');
33470             
33471             if(i.value === v || i.value.toString() === v.toString()){
33472                 i.checked = true;
33473                 i.el.addClass('checked');
33474                 
33475                 if(suppressEvent !== true){
33476                     this.fireEvent('check', this, i);
33477                 }
33478             }
33479             
33480         }, this);
33481         
33482         this.validate();
33483     },
33484     
33485     clearInvalid : function(){
33486         
33487         if(!this.el || this.preventMark){
33488             return;
33489         }
33490         
33491         this.el.removeClass([this.invalidClass]);
33492         
33493         this.fireEvent('valid', this);
33494     }
33495     
33496 });
33497
33498 Roo.apply(Roo.bootstrap.RadioSet, {
33499     
33500     groups: {},
33501     
33502     register : function(set)
33503     {
33504         this.groups[set.name] = set;
33505     },
33506     
33507     get: function(name) 
33508     {
33509         if (typeof(this.groups[name]) == 'undefined') {
33510             return false;
33511         }
33512         
33513         return this.groups[name] ;
33514     }
33515     
33516 });
33517 /*
33518  * Based on:
33519  * Ext JS Library 1.1.1
33520  * Copyright(c) 2006-2007, Ext JS, LLC.
33521  *
33522  * Originally Released Under LGPL - original licence link has changed is not relivant.
33523  *
33524  * Fork - LGPL
33525  * <script type="text/javascript">
33526  */
33527
33528
33529 /**
33530  * @class Roo.bootstrap.SplitBar
33531  * @extends Roo.util.Observable
33532  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33533  * <br><br>
33534  * Usage:
33535  * <pre><code>
33536 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33537                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33538 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33539 split.minSize = 100;
33540 split.maxSize = 600;
33541 split.animate = true;
33542 split.on('moved', splitterMoved);
33543 </code></pre>
33544  * @constructor
33545  * Create a new SplitBar
33546  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33547  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33548  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33549  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33550                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33551                         position of the SplitBar).
33552  */
33553 Roo.bootstrap.SplitBar = function(cfg){
33554     
33555     /** @private */
33556     
33557     //{
33558     //  dragElement : elm
33559     //  resizingElement: el,
33560         // optional..
33561     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33562     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33563         // existingProxy ???
33564     //}
33565     
33566     this.el = Roo.get(cfg.dragElement, true);
33567     this.el.dom.unselectable = "on";
33568     /** @private */
33569     this.resizingEl = Roo.get(cfg.resizingElement, true);
33570
33571     /**
33572      * @private
33573      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33574      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33575      * @type Number
33576      */
33577     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33578     
33579     /**
33580      * The minimum size of the resizing element. (Defaults to 0)
33581      * @type Number
33582      */
33583     this.minSize = 0;
33584     
33585     /**
33586      * The maximum size of the resizing element. (Defaults to 2000)
33587      * @type Number
33588      */
33589     this.maxSize = 2000;
33590     
33591     /**
33592      * Whether to animate the transition to the new size
33593      * @type Boolean
33594      */
33595     this.animate = false;
33596     
33597     /**
33598      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33599      * @type Boolean
33600      */
33601     this.useShim = false;
33602     
33603     /** @private */
33604     this.shim = null;
33605     
33606     if(!cfg.existingProxy){
33607         /** @private */
33608         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33609     }else{
33610         this.proxy = Roo.get(cfg.existingProxy).dom;
33611     }
33612     /** @private */
33613     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33614     
33615     /** @private */
33616     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33617     
33618     /** @private */
33619     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33620     
33621     /** @private */
33622     this.dragSpecs = {};
33623     
33624     /**
33625      * @private The adapter to use to positon and resize elements
33626      */
33627     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33628     this.adapter.init(this);
33629     
33630     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33631         /** @private */
33632         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33633         this.el.addClass("roo-splitbar-h");
33634     }else{
33635         /** @private */
33636         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33637         this.el.addClass("roo-splitbar-v");
33638     }
33639     
33640     this.addEvents({
33641         /**
33642          * @event resize
33643          * Fires when the splitter is moved (alias for {@link #event-moved})
33644          * @param {Roo.bootstrap.SplitBar} this
33645          * @param {Number} newSize the new width or height
33646          */
33647         "resize" : true,
33648         /**
33649          * @event moved
33650          * Fires when the splitter is moved
33651          * @param {Roo.bootstrap.SplitBar} this
33652          * @param {Number} newSize the new width or height
33653          */
33654         "moved" : true,
33655         /**
33656          * @event beforeresize
33657          * Fires before the splitter is dragged
33658          * @param {Roo.bootstrap.SplitBar} this
33659          */
33660         "beforeresize" : true,
33661
33662         "beforeapply" : true
33663     });
33664
33665     Roo.util.Observable.call(this);
33666 };
33667
33668 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33669     onStartProxyDrag : function(x, y){
33670         this.fireEvent("beforeresize", this);
33671         if(!this.overlay){
33672             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33673             o.unselectable();
33674             o.enableDisplayMode("block");
33675             // all splitbars share the same overlay
33676             Roo.bootstrap.SplitBar.prototype.overlay = o;
33677         }
33678         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33679         this.overlay.show();
33680         Roo.get(this.proxy).setDisplayed("block");
33681         var size = this.adapter.getElementSize(this);
33682         this.activeMinSize = this.getMinimumSize();;
33683         this.activeMaxSize = this.getMaximumSize();;
33684         var c1 = size - this.activeMinSize;
33685         var c2 = Math.max(this.activeMaxSize - size, 0);
33686         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33687             this.dd.resetConstraints();
33688             this.dd.setXConstraint(
33689                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33690                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33691             );
33692             this.dd.setYConstraint(0, 0);
33693         }else{
33694             this.dd.resetConstraints();
33695             this.dd.setXConstraint(0, 0);
33696             this.dd.setYConstraint(
33697                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33698                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33699             );
33700          }
33701         this.dragSpecs.startSize = size;
33702         this.dragSpecs.startPoint = [x, y];
33703         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33704     },
33705     
33706     /** 
33707      * @private Called after the drag operation by the DDProxy
33708      */
33709     onEndProxyDrag : function(e){
33710         Roo.get(this.proxy).setDisplayed(false);
33711         var endPoint = Roo.lib.Event.getXY(e);
33712         if(this.overlay){
33713             this.overlay.hide();
33714         }
33715         var newSize;
33716         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33717             newSize = this.dragSpecs.startSize + 
33718                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33719                     endPoint[0] - this.dragSpecs.startPoint[0] :
33720                     this.dragSpecs.startPoint[0] - endPoint[0]
33721                 );
33722         }else{
33723             newSize = this.dragSpecs.startSize + 
33724                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33725                     endPoint[1] - this.dragSpecs.startPoint[1] :
33726                     this.dragSpecs.startPoint[1] - endPoint[1]
33727                 );
33728         }
33729         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33730         if(newSize != this.dragSpecs.startSize){
33731             if(this.fireEvent('beforeapply', this, newSize) !== false){
33732                 this.adapter.setElementSize(this, newSize);
33733                 this.fireEvent("moved", this, newSize);
33734                 this.fireEvent("resize", this, newSize);
33735             }
33736         }
33737     },
33738     
33739     /**
33740      * Get the adapter this SplitBar uses
33741      * @return The adapter object
33742      */
33743     getAdapter : function(){
33744         return this.adapter;
33745     },
33746     
33747     /**
33748      * Set the adapter this SplitBar uses
33749      * @param {Object} adapter A SplitBar adapter object
33750      */
33751     setAdapter : function(adapter){
33752         this.adapter = adapter;
33753         this.adapter.init(this);
33754     },
33755     
33756     /**
33757      * Gets the minimum size for the resizing element
33758      * @return {Number} The minimum size
33759      */
33760     getMinimumSize : function(){
33761         return this.minSize;
33762     },
33763     
33764     /**
33765      * Sets the minimum size for the resizing element
33766      * @param {Number} minSize The minimum size
33767      */
33768     setMinimumSize : function(minSize){
33769         this.minSize = minSize;
33770     },
33771     
33772     /**
33773      * Gets the maximum size for the resizing element
33774      * @return {Number} The maximum size
33775      */
33776     getMaximumSize : function(){
33777         return this.maxSize;
33778     },
33779     
33780     /**
33781      * Sets the maximum size for the resizing element
33782      * @param {Number} maxSize The maximum size
33783      */
33784     setMaximumSize : function(maxSize){
33785         this.maxSize = maxSize;
33786     },
33787     
33788     /**
33789      * Sets the initialize size for the resizing element
33790      * @param {Number} size The initial size
33791      */
33792     setCurrentSize : function(size){
33793         var oldAnimate = this.animate;
33794         this.animate = false;
33795         this.adapter.setElementSize(this, size);
33796         this.animate = oldAnimate;
33797     },
33798     
33799     /**
33800      * Destroy this splitbar. 
33801      * @param {Boolean} removeEl True to remove the element
33802      */
33803     destroy : function(removeEl){
33804         if(this.shim){
33805             this.shim.remove();
33806         }
33807         this.dd.unreg();
33808         this.proxy.parentNode.removeChild(this.proxy);
33809         if(removeEl){
33810             this.el.remove();
33811         }
33812     }
33813 });
33814
33815 /**
33816  * @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.
33817  */
33818 Roo.bootstrap.SplitBar.createProxy = function(dir){
33819     var proxy = new Roo.Element(document.createElement("div"));
33820     proxy.unselectable();
33821     var cls = 'roo-splitbar-proxy';
33822     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33823     document.body.appendChild(proxy.dom);
33824     return proxy.dom;
33825 };
33826
33827 /** 
33828  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33829  * Default Adapter. It assumes the splitter and resizing element are not positioned
33830  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33831  */
33832 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33833 };
33834
33835 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33836     // do nothing for now
33837     init : function(s){
33838     
33839     },
33840     /**
33841      * Called before drag operations to get the current size of the resizing element. 
33842      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33843      */
33844      getElementSize : function(s){
33845         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33846             return s.resizingEl.getWidth();
33847         }else{
33848             return s.resizingEl.getHeight();
33849         }
33850     },
33851     
33852     /**
33853      * Called after drag operations to set the size of the resizing element.
33854      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33855      * @param {Number} newSize The new size to set
33856      * @param {Function} onComplete A function to be invoked when resizing is complete
33857      */
33858     setElementSize : function(s, newSize, onComplete){
33859         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33860             if(!s.animate){
33861                 s.resizingEl.setWidth(newSize);
33862                 if(onComplete){
33863                     onComplete(s, newSize);
33864                 }
33865             }else{
33866                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33867             }
33868         }else{
33869             
33870             if(!s.animate){
33871                 s.resizingEl.setHeight(newSize);
33872                 if(onComplete){
33873                     onComplete(s, newSize);
33874                 }
33875             }else{
33876                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33877             }
33878         }
33879     }
33880 };
33881
33882 /** 
33883  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33884  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33885  * Adapter that  moves the splitter element to align with the resized sizing element. 
33886  * Used with an absolute positioned SplitBar.
33887  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33888  * document.body, make sure you assign an id to the body element.
33889  */
33890 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33891     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33892     this.container = Roo.get(container);
33893 };
33894
33895 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33896     init : function(s){
33897         this.basic.init(s);
33898     },
33899     
33900     getElementSize : function(s){
33901         return this.basic.getElementSize(s);
33902     },
33903     
33904     setElementSize : function(s, newSize, onComplete){
33905         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33906     },
33907     
33908     moveSplitter : function(s){
33909         var yes = Roo.bootstrap.SplitBar;
33910         switch(s.placement){
33911             case yes.LEFT:
33912                 s.el.setX(s.resizingEl.getRight());
33913                 break;
33914             case yes.RIGHT:
33915                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33916                 break;
33917             case yes.TOP:
33918                 s.el.setY(s.resizingEl.getBottom());
33919                 break;
33920             case yes.BOTTOM:
33921                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33922                 break;
33923         }
33924     }
33925 };
33926
33927 /**
33928  * Orientation constant - Create a vertical SplitBar
33929  * @static
33930  * @type Number
33931  */
33932 Roo.bootstrap.SplitBar.VERTICAL = 1;
33933
33934 /**
33935  * Orientation constant - Create a horizontal SplitBar
33936  * @static
33937  * @type Number
33938  */
33939 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33940
33941 /**
33942  * Placement constant - The resizing element is to the left of the splitter element
33943  * @static
33944  * @type Number
33945  */
33946 Roo.bootstrap.SplitBar.LEFT = 1;
33947
33948 /**
33949  * Placement constant - The resizing element is to the right of the splitter element
33950  * @static
33951  * @type Number
33952  */
33953 Roo.bootstrap.SplitBar.RIGHT = 2;
33954
33955 /**
33956  * Placement constant - The resizing element is positioned above the splitter element
33957  * @static
33958  * @type Number
33959  */
33960 Roo.bootstrap.SplitBar.TOP = 3;
33961
33962 /**
33963  * Placement constant - The resizing element is positioned under splitter element
33964  * @static
33965  * @type Number
33966  */
33967 Roo.bootstrap.SplitBar.BOTTOM = 4;
33968 Roo.namespace("Roo.bootstrap.layout");/*
33969  * Based on:
33970  * Ext JS Library 1.1.1
33971  * Copyright(c) 2006-2007, Ext JS, LLC.
33972  *
33973  * Originally Released Under LGPL - original licence link has changed is not relivant.
33974  *
33975  * Fork - LGPL
33976  * <script type="text/javascript">
33977  */
33978
33979 /**
33980  * @class Roo.bootstrap.layout.Manager
33981  * @extends Roo.bootstrap.Component
33982  * Base class for layout managers.
33983  */
33984 Roo.bootstrap.layout.Manager = function(config)
33985 {
33986     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33987
33988
33989
33990
33991
33992     /** false to disable window resize monitoring @type Boolean */
33993     this.monitorWindowResize = true;
33994     this.regions = {};
33995     this.addEvents({
33996         /**
33997          * @event layout
33998          * Fires when a layout is performed.
33999          * @param {Roo.LayoutManager} this
34000          */
34001         "layout" : true,
34002         /**
34003          * @event regionresized
34004          * Fires when the user resizes a region.
34005          * @param {Roo.LayoutRegion} region The resized region
34006          * @param {Number} newSize The new size (width for east/west, height for north/south)
34007          */
34008         "regionresized" : true,
34009         /**
34010          * @event regioncollapsed
34011          * Fires when a region is collapsed.
34012          * @param {Roo.LayoutRegion} region The collapsed region
34013          */
34014         "regioncollapsed" : true,
34015         /**
34016          * @event regionexpanded
34017          * Fires when a region is expanded.
34018          * @param {Roo.LayoutRegion} region The expanded region
34019          */
34020         "regionexpanded" : true
34021     });
34022     this.updating = false;
34023
34024     if (config.el) {
34025         this.el = Roo.get(config.el);
34026         this.initEvents();
34027     }
34028
34029 };
34030
34031 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34032
34033
34034     regions : null,
34035
34036     monitorWindowResize : true,
34037
34038
34039     updating : false,
34040
34041
34042     onRender : function(ct, position)
34043     {
34044         if(!this.el){
34045             this.el = Roo.get(ct);
34046             this.initEvents();
34047         }
34048         //this.fireEvent('render',this);
34049     },
34050
34051
34052     initEvents: function()
34053     {
34054
34055
34056         // ie scrollbar fix
34057         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34058             document.body.scroll = "no";
34059         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34060             this.el.position('relative');
34061         }
34062         this.id = this.el.id;
34063         this.el.addClass("roo-layout-container");
34064         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34065         if(this.el.dom != document.body ) {
34066             this.el.on('resize', this.layout,this);
34067             this.el.on('show', this.layout,this);
34068         }
34069
34070     },
34071
34072     /**
34073      * Returns true if this layout is currently being updated
34074      * @return {Boolean}
34075      */
34076     isUpdating : function(){
34077         return this.updating;
34078     },
34079
34080     /**
34081      * Suspend the LayoutManager from doing auto-layouts while
34082      * making multiple add or remove calls
34083      */
34084     beginUpdate : function(){
34085         this.updating = true;
34086     },
34087
34088     /**
34089      * Restore auto-layouts and optionally disable the manager from performing a layout
34090      * @param {Boolean} noLayout true to disable a layout update
34091      */
34092     endUpdate : function(noLayout){
34093         this.updating = false;
34094         if(!noLayout){
34095             this.layout();
34096         }
34097     },
34098
34099     layout: function(){
34100         // abstract...
34101     },
34102
34103     onRegionResized : function(region, newSize){
34104         this.fireEvent("regionresized", region, newSize);
34105         this.layout();
34106     },
34107
34108     onRegionCollapsed : function(region){
34109         this.fireEvent("regioncollapsed", region);
34110     },
34111
34112     onRegionExpanded : function(region){
34113         this.fireEvent("regionexpanded", region);
34114     },
34115
34116     /**
34117      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34118      * performs box-model adjustments.
34119      * @return {Object} The size as an object {width: (the width), height: (the height)}
34120      */
34121     getViewSize : function()
34122     {
34123         var size;
34124         if(this.el.dom != document.body){
34125             size = this.el.getSize();
34126         }else{
34127             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34128         }
34129         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34130         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34131         return size;
34132     },
34133
34134     /**
34135      * Returns the Element this layout is bound to.
34136      * @return {Roo.Element}
34137      */
34138     getEl : function(){
34139         return this.el;
34140     },
34141
34142     /**
34143      * Returns the specified region.
34144      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34145      * @return {Roo.LayoutRegion}
34146      */
34147     getRegion : function(target){
34148         return this.regions[target.toLowerCase()];
34149     },
34150
34151     onWindowResize : function(){
34152         if(this.monitorWindowResize){
34153             this.layout();
34154         }
34155     }
34156 });
34157 /*
34158  * Based on:
34159  * Ext JS Library 1.1.1
34160  * Copyright(c) 2006-2007, Ext JS, LLC.
34161  *
34162  * Originally Released Under LGPL - original licence link has changed is not relivant.
34163  *
34164  * Fork - LGPL
34165  * <script type="text/javascript">
34166  */
34167 /**
34168  * @class Roo.bootstrap.layout.Border
34169  * @extends Roo.bootstrap.layout.Manager
34170  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34171  * please see: examples/bootstrap/nested.html<br><br>
34172  
34173 <b>The container the layout is rendered into can be either the body element or any other element.
34174 If it is not the body element, the container needs to either be an absolute positioned element,
34175 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34176 the container size if it is not the body element.</b>
34177
34178 * @constructor
34179 * Create a new Border
34180 * @param {Object} config Configuration options
34181  */
34182 Roo.bootstrap.layout.Border = function(config){
34183     config = config || {};
34184     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34185     
34186     
34187     
34188     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34189         if(config[region]){
34190             config[region].region = region;
34191             this.addRegion(config[region]);
34192         }
34193     },this);
34194     
34195 };
34196
34197 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34198
34199 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34200     /**
34201      * Creates and adds a new region if it doesn't already exist.
34202      * @param {String} target The target region key (north, south, east, west or center).
34203      * @param {Object} config The regions config object
34204      * @return {BorderLayoutRegion} The new region
34205      */
34206     addRegion : function(config)
34207     {
34208         if(!this.regions[config.region]){
34209             var r = this.factory(config);
34210             this.bindRegion(r);
34211         }
34212         return this.regions[config.region];
34213     },
34214
34215     // private (kinda)
34216     bindRegion : function(r){
34217         this.regions[r.config.region] = r;
34218         
34219         r.on("visibilitychange",    this.layout, this);
34220         r.on("paneladded",          this.layout, this);
34221         r.on("panelremoved",        this.layout, this);
34222         r.on("invalidated",         this.layout, this);
34223         r.on("resized",             this.onRegionResized, this);
34224         r.on("collapsed",           this.onRegionCollapsed, this);
34225         r.on("expanded",            this.onRegionExpanded, this);
34226     },
34227
34228     /**
34229      * Performs a layout update.
34230      */
34231     layout : function()
34232     {
34233         if(this.updating) {
34234             return;
34235         }
34236         
34237         // render all the rebions if they have not been done alreayd?
34238         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34239             if(this.regions[region] && !this.regions[region].bodyEl){
34240                 this.regions[region].onRender(this.el)
34241             }
34242         },this);
34243         
34244         var size = this.getViewSize();
34245         var w = size.width;
34246         var h = size.height;
34247         var centerW = w;
34248         var centerH = h;
34249         var centerY = 0;
34250         var centerX = 0;
34251         //var x = 0, y = 0;
34252
34253         var rs = this.regions;
34254         var north = rs["north"];
34255         var south = rs["south"]; 
34256         var west = rs["west"];
34257         var east = rs["east"];
34258         var center = rs["center"];
34259         //if(this.hideOnLayout){ // not supported anymore
34260             //c.el.setStyle("display", "none");
34261         //}
34262         if(north && north.isVisible()){
34263             var b = north.getBox();
34264             var m = north.getMargins();
34265             b.width = w - (m.left+m.right);
34266             b.x = m.left;
34267             b.y = m.top;
34268             centerY = b.height + b.y + m.bottom;
34269             centerH -= centerY;
34270             north.updateBox(this.safeBox(b));
34271         }
34272         if(south && south.isVisible()){
34273             var b = south.getBox();
34274             var m = south.getMargins();
34275             b.width = w - (m.left+m.right);
34276             b.x = m.left;
34277             var totalHeight = (b.height + m.top + m.bottom);
34278             b.y = h - totalHeight + m.top;
34279             centerH -= totalHeight;
34280             south.updateBox(this.safeBox(b));
34281         }
34282         if(west && west.isVisible()){
34283             var b = west.getBox();
34284             var m = west.getMargins();
34285             b.height = centerH - (m.top+m.bottom);
34286             b.x = m.left;
34287             b.y = centerY + m.top;
34288             var totalWidth = (b.width + m.left + m.right);
34289             centerX += totalWidth;
34290             centerW -= totalWidth;
34291             west.updateBox(this.safeBox(b));
34292         }
34293         if(east && east.isVisible()){
34294             var b = east.getBox();
34295             var m = east.getMargins();
34296             b.height = centerH - (m.top+m.bottom);
34297             var totalWidth = (b.width + m.left + m.right);
34298             b.x = w - totalWidth + m.left;
34299             b.y = centerY + m.top;
34300             centerW -= totalWidth;
34301             east.updateBox(this.safeBox(b));
34302         }
34303         if(center){
34304             var m = center.getMargins();
34305             var centerBox = {
34306                 x: centerX + m.left,
34307                 y: centerY + m.top,
34308                 width: centerW - (m.left+m.right),
34309                 height: centerH - (m.top+m.bottom)
34310             };
34311             //if(this.hideOnLayout){
34312                 //center.el.setStyle("display", "block");
34313             //}
34314             center.updateBox(this.safeBox(centerBox));
34315         }
34316         this.el.repaint();
34317         this.fireEvent("layout", this);
34318     },
34319
34320     // private
34321     safeBox : function(box){
34322         box.width = Math.max(0, box.width);
34323         box.height = Math.max(0, box.height);
34324         return box;
34325     },
34326
34327     /**
34328      * Adds a ContentPanel (or subclass) to this layout.
34329      * @param {String} target The target region key (north, south, east, west or center).
34330      * @param {Roo.ContentPanel} panel The panel to add
34331      * @return {Roo.ContentPanel} The added panel
34332      */
34333     add : function(target, panel){
34334          
34335         target = target.toLowerCase();
34336         return this.regions[target].add(panel);
34337     },
34338
34339     /**
34340      * Remove a ContentPanel (or subclass) to this layout.
34341      * @param {String} target The target region key (north, south, east, west or center).
34342      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34343      * @return {Roo.ContentPanel} The removed panel
34344      */
34345     remove : function(target, panel){
34346         target = target.toLowerCase();
34347         return this.regions[target].remove(panel);
34348     },
34349
34350     /**
34351      * Searches all regions for a panel with the specified id
34352      * @param {String} panelId
34353      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34354      */
34355     findPanel : function(panelId){
34356         var rs = this.regions;
34357         for(var target in rs){
34358             if(typeof rs[target] != "function"){
34359                 var p = rs[target].getPanel(panelId);
34360                 if(p){
34361                     return p;
34362                 }
34363             }
34364         }
34365         return null;
34366     },
34367
34368     /**
34369      * Searches all regions for a panel with the specified id and activates (shows) it.
34370      * @param {String/ContentPanel} panelId The panels id or the panel itself
34371      * @return {Roo.ContentPanel} The shown panel or null
34372      */
34373     showPanel : function(panelId) {
34374       var rs = this.regions;
34375       for(var target in rs){
34376          var r = rs[target];
34377          if(typeof r != "function"){
34378             if(r.hasPanel(panelId)){
34379                return r.showPanel(panelId);
34380             }
34381          }
34382       }
34383       return null;
34384    },
34385
34386    /**
34387      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34388      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34389      */
34390    /*
34391     restoreState : function(provider){
34392         if(!provider){
34393             provider = Roo.state.Manager;
34394         }
34395         var sm = new Roo.LayoutStateManager();
34396         sm.init(this, provider);
34397     },
34398 */
34399  
34400  
34401     /**
34402      * Adds a xtype elements to the layout.
34403      * <pre><code>
34404
34405 layout.addxtype({
34406        xtype : 'ContentPanel',
34407        region: 'west',
34408        items: [ .... ]
34409    }
34410 );
34411
34412 layout.addxtype({
34413         xtype : 'NestedLayoutPanel',
34414         region: 'west',
34415         layout: {
34416            center: { },
34417            west: { }   
34418         },
34419         items : [ ... list of content panels or nested layout panels.. ]
34420    }
34421 );
34422 </code></pre>
34423      * @param {Object} cfg Xtype definition of item to add.
34424      */
34425     addxtype : function(cfg)
34426     {
34427         // basically accepts a pannel...
34428         // can accept a layout region..!?!?
34429         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34430         
34431         
34432         // theory?  children can only be panels??
34433         
34434         //if (!cfg.xtype.match(/Panel$/)) {
34435         //    return false;
34436         //}
34437         var ret = false;
34438         
34439         if (typeof(cfg.region) == 'undefined') {
34440             Roo.log("Failed to add Panel, region was not set");
34441             Roo.log(cfg);
34442             return false;
34443         }
34444         var region = cfg.region;
34445         delete cfg.region;
34446         
34447           
34448         var xitems = [];
34449         if (cfg.items) {
34450             xitems = cfg.items;
34451             delete cfg.items;
34452         }
34453         var nb = false;
34454         
34455         switch(cfg.xtype) 
34456         {
34457             case 'Content':  // ContentPanel (el, cfg)
34458             case 'Scroll':  // ContentPanel (el, cfg)
34459             case 'View': 
34460                 cfg.autoCreate = true;
34461                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34462                 //} else {
34463                 //    var el = this.el.createChild();
34464                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34465                 //}
34466                 
34467                 this.add(region, ret);
34468                 break;
34469             
34470             /*
34471             case 'TreePanel': // our new panel!
34472                 cfg.el = this.el.createChild();
34473                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34474                 this.add(region, ret);
34475                 break;
34476             */
34477             
34478             case 'Nest': 
34479                 // create a new Layout (which is  a Border Layout...
34480                 
34481                 var clayout = cfg.layout;
34482                 clayout.el  = this.el.createChild();
34483                 clayout.items   = clayout.items  || [];
34484                 
34485                 delete cfg.layout;
34486                 
34487                 // replace this exitems with the clayout ones..
34488                 xitems = clayout.items;
34489                  
34490                 // force background off if it's in center...
34491                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34492                     cfg.background = false;
34493                 }
34494                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34495                 
34496                 
34497                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34498                 //console.log('adding nested layout panel '  + cfg.toSource());
34499                 this.add(region, ret);
34500                 nb = {}; /// find first...
34501                 break;
34502             
34503             case 'Grid':
34504                 
34505                 // needs grid and region
34506                 
34507                 //var el = this.getRegion(region).el.createChild();
34508                 /*
34509                  *var el = this.el.createChild();
34510                 // create the grid first...
34511                 cfg.grid.container = el;
34512                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34513                 */
34514                 
34515                 if (region == 'center' && this.active ) {
34516                     cfg.background = false;
34517                 }
34518                 
34519                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34520                 
34521                 this.add(region, ret);
34522                 /*
34523                 if (cfg.background) {
34524                     // render grid on panel activation (if panel background)
34525                     ret.on('activate', function(gp) {
34526                         if (!gp.grid.rendered) {
34527                     //        gp.grid.render(el);
34528                         }
34529                     });
34530                 } else {
34531                   //  cfg.grid.render(el);
34532                 }
34533                 */
34534                 break;
34535            
34536            
34537             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34538                 // it was the old xcomponent building that caused this before.
34539                 // espeically if border is the top element in the tree.
34540                 ret = this;
34541                 break; 
34542                 
34543                     
34544                 
34545                 
34546                 
34547             default:
34548                 /*
34549                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34550                     
34551                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34552                     this.add(region, ret);
34553                 } else {
34554                 */
34555                     Roo.log(cfg);
34556                     throw "Can not add '" + cfg.xtype + "' to Border";
34557                     return null;
34558              
34559                                 
34560              
34561         }
34562         this.beginUpdate();
34563         // add children..
34564         var region = '';
34565         var abn = {};
34566         Roo.each(xitems, function(i)  {
34567             region = nb && i.region ? i.region : false;
34568             
34569             var add = ret.addxtype(i);
34570            
34571             if (region) {
34572                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34573                 if (!i.background) {
34574                     abn[region] = nb[region] ;
34575                 }
34576             }
34577             
34578         });
34579         this.endUpdate();
34580
34581         // make the last non-background panel active..
34582         //if (nb) { Roo.log(abn); }
34583         if (nb) {
34584             
34585             for(var r in abn) {
34586                 region = this.getRegion(r);
34587                 if (region) {
34588                     // tried using nb[r], but it does not work..
34589                      
34590                     region.showPanel(abn[r]);
34591                    
34592                 }
34593             }
34594         }
34595         return ret;
34596         
34597     },
34598     
34599     
34600 // private
34601     factory : function(cfg)
34602     {
34603         
34604         var validRegions = Roo.bootstrap.layout.Border.regions;
34605
34606         var target = cfg.region;
34607         cfg.mgr = this;
34608         
34609         var r = Roo.bootstrap.layout;
34610         Roo.log(target);
34611         switch(target){
34612             case "north":
34613                 return new r.North(cfg);
34614             case "south":
34615                 return new r.South(cfg);
34616             case "east":
34617                 return new r.East(cfg);
34618             case "west":
34619                 return new r.West(cfg);
34620             case "center":
34621                 return new r.Center(cfg);
34622         }
34623         throw 'Layout region "'+target+'" not supported.';
34624     }
34625     
34626     
34627 });
34628  /*
34629  * Based on:
34630  * Ext JS Library 1.1.1
34631  * Copyright(c) 2006-2007, Ext JS, LLC.
34632  *
34633  * Originally Released Under LGPL - original licence link has changed is not relivant.
34634  *
34635  * Fork - LGPL
34636  * <script type="text/javascript">
34637  */
34638  
34639 /**
34640  * @class Roo.bootstrap.layout.Basic
34641  * @extends Roo.util.Observable
34642  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34643  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34644  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34645  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34646  * @cfg {string}   region  the region that it inhabits..
34647  * @cfg {bool}   skipConfig skip config?
34648  * 
34649
34650  */
34651 Roo.bootstrap.layout.Basic = function(config){
34652     
34653     this.mgr = config.mgr;
34654     
34655     this.position = config.region;
34656     
34657     var skipConfig = config.skipConfig;
34658     
34659     this.events = {
34660         /**
34661          * @scope Roo.BasicLayoutRegion
34662          */
34663         
34664         /**
34665          * @event beforeremove
34666          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34667          * @param {Roo.LayoutRegion} this
34668          * @param {Roo.ContentPanel} panel The panel
34669          * @param {Object} e The cancel event object
34670          */
34671         "beforeremove" : true,
34672         /**
34673          * @event invalidated
34674          * Fires when the layout for this region is changed.
34675          * @param {Roo.LayoutRegion} this
34676          */
34677         "invalidated" : true,
34678         /**
34679          * @event visibilitychange
34680          * Fires when this region is shown or hidden 
34681          * @param {Roo.LayoutRegion} this
34682          * @param {Boolean} visibility true or false
34683          */
34684         "visibilitychange" : true,
34685         /**
34686          * @event paneladded
34687          * Fires when a panel is added. 
34688          * @param {Roo.LayoutRegion} this
34689          * @param {Roo.ContentPanel} panel The panel
34690          */
34691         "paneladded" : true,
34692         /**
34693          * @event panelremoved
34694          * Fires when a panel is removed. 
34695          * @param {Roo.LayoutRegion} this
34696          * @param {Roo.ContentPanel} panel The panel
34697          */
34698         "panelremoved" : true,
34699         /**
34700          * @event beforecollapse
34701          * Fires when this region before collapse.
34702          * @param {Roo.LayoutRegion} this
34703          */
34704         "beforecollapse" : true,
34705         /**
34706          * @event collapsed
34707          * Fires when this region is collapsed.
34708          * @param {Roo.LayoutRegion} this
34709          */
34710         "collapsed" : true,
34711         /**
34712          * @event expanded
34713          * Fires when this region is expanded.
34714          * @param {Roo.LayoutRegion} this
34715          */
34716         "expanded" : true,
34717         /**
34718          * @event slideshow
34719          * Fires when this region is slid into view.
34720          * @param {Roo.LayoutRegion} this
34721          */
34722         "slideshow" : true,
34723         /**
34724          * @event slidehide
34725          * Fires when this region slides out of view. 
34726          * @param {Roo.LayoutRegion} this
34727          */
34728         "slidehide" : true,
34729         /**
34730          * @event panelactivated
34731          * Fires when a panel is activated. 
34732          * @param {Roo.LayoutRegion} this
34733          * @param {Roo.ContentPanel} panel The activated panel
34734          */
34735         "panelactivated" : true,
34736         /**
34737          * @event resized
34738          * Fires when the user resizes this region. 
34739          * @param {Roo.LayoutRegion} this
34740          * @param {Number} newSize The new size (width for east/west, height for north/south)
34741          */
34742         "resized" : true
34743     };
34744     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34745     this.panels = new Roo.util.MixedCollection();
34746     this.panels.getKey = this.getPanelId.createDelegate(this);
34747     this.box = null;
34748     this.activePanel = null;
34749     // ensure listeners are added...
34750     
34751     if (config.listeners || config.events) {
34752         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34753             listeners : config.listeners || {},
34754             events : config.events || {}
34755         });
34756     }
34757     
34758     if(skipConfig !== true){
34759         this.applyConfig(config);
34760     }
34761 };
34762
34763 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34764 {
34765     getPanelId : function(p){
34766         return p.getId();
34767     },
34768     
34769     applyConfig : function(config){
34770         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34771         this.config = config;
34772         
34773     },
34774     
34775     /**
34776      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34777      * the width, for horizontal (north, south) the height.
34778      * @param {Number} newSize The new width or height
34779      */
34780     resizeTo : function(newSize){
34781         var el = this.el ? this.el :
34782                  (this.activePanel ? this.activePanel.getEl() : null);
34783         if(el){
34784             switch(this.position){
34785                 case "east":
34786                 case "west":
34787                     el.setWidth(newSize);
34788                     this.fireEvent("resized", this, newSize);
34789                 break;
34790                 case "north":
34791                 case "south":
34792                     el.setHeight(newSize);
34793                     this.fireEvent("resized", this, newSize);
34794                 break;                
34795             }
34796         }
34797     },
34798     
34799     getBox : function(){
34800         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34801     },
34802     
34803     getMargins : function(){
34804         return this.margins;
34805     },
34806     
34807     updateBox : function(box){
34808         this.box = box;
34809         var el = this.activePanel.getEl();
34810         el.dom.style.left = box.x + "px";
34811         el.dom.style.top = box.y + "px";
34812         this.activePanel.setSize(box.width, box.height);
34813     },
34814     
34815     /**
34816      * Returns the container element for this region.
34817      * @return {Roo.Element}
34818      */
34819     getEl : function(){
34820         return this.activePanel;
34821     },
34822     
34823     /**
34824      * Returns true if this region is currently visible.
34825      * @return {Boolean}
34826      */
34827     isVisible : function(){
34828         return this.activePanel ? true : false;
34829     },
34830     
34831     setActivePanel : function(panel){
34832         panel = this.getPanel(panel);
34833         if(this.activePanel && this.activePanel != panel){
34834             this.activePanel.setActiveState(false);
34835             this.activePanel.getEl().setLeftTop(-10000,-10000);
34836         }
34837         this.activePanel = panel;
34838         panel.setActiveState(true);
34839         if(this.box){
34840             panel.setSize(this.box.width, this.box.height);
34841         }
34842         this.fireEvent("panelactivated", this, panel);
34843         this.fireEvent("invalidated");
34844     },
34845     
34846     /**
34847      * Show the specified panel.
34848      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34849      * @return {Roo.ContentPanel} The shown panel or null
34850      */
34851     showPanel : function(panel){
34852         panel = this.getPanel(panel);
34853         if(panel){
34854             this.setActivePanel(panel);
34855         }
34856         return panel;
34857     },
34858     
34859     /**
34860      * Get the active panel for this region.
34861      * @return {Roo.ContentPanel} The active panel or null
34862      */
34863     getActivePanel : function(){
34864         return this.activePanel;
34865     },
34866     
34867     /**
34868      * Add the passed ContentPanel(s)
34869      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34870      * @return {Roo.ContentPanel} The panel added (if only one was added)
34871      */
34872     add : function(panel){
34873         if(arguments.length > 1){
34874             for(var i = 0, len = arguments.length; i < len; i++) {
34875                 this.add(arguments[i]);
34876             }
34877             return null;
34878         }
34879         if(this.hasPanel(panel)){
34880             this.showPanel(panel);
34881             return panel;
34882         }
34883         var el = panel.getEl();
34884         if(el.dom.parentNode != this.mgr.el.dom){
34885             this.mgr.el.dom.appendChild(el.dom);
34886         }
34887         if(panel.setRegion){
34888             panel.setRegion(this);
34889         }
34890         this.panels.add(panel);
34891         el.setStyle("position", "absolute");
34892         if(!panel.background){
34893             this.setActivePanel(panel);
34894             if(this.config.initialSize && this.panels.getCount()==1){
34895                 this.resizeTo(this.config.initialSize);
34896             }
34897         }
34898         this.fireEvent("paneladded", this, panel);
34899         return panel;
34900     },
34901     
34902     /**
34903      * Returns true if the panel is in this region.
34904      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34905      * @return {Boolean}
34906      */
34907     hasPanel : function(panel){
34908         if(typeof panel == "object"){ // must be panel obj
34909             panel = panel.getId();
34910         }
34911         return this.getPanel(panel) ? true : false;
34912     },
34913     
34914     /**
34915      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34916      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34917      * @param {Boolean} preservePanel Overrides the config preservePanel option
34918      * @return {Roo.ContentPanel} The panel that was removed
34919      */
34920     remove : function(panel, preservePanel){
34921         panel = this.getPanel(panel);
34922         if(!panel){
34923             return null;
34924         }
34925         var e = {};
34926         this.fireEvent("beforeremove", this, panel, e);
34927         if(e.cancel === true){
34928             return null;
34929         }
34930         var panelId = panel.getId();
34931         this.panels.removeKey(panelId);
34932         return panel;
34933     },
34934     
34935     /**
34936      * Returns the panel specified or null if it's not in this region.
34937      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34938      * @return {Roo.ContentPanel}
34939      */
34940     getPanel : function(id){
34941         if(typeof id == "object"){ // must be panel obj
34942             return id;
34943         }
34944         return this.panels.get(id);
34945     },
34946     
34947     /**
34948      * Returns this regions position (north/south/east/west/center).
34949      * @return {String} 
34950      */
34951     getPosition: function(){
34952         return this.position;    
34953     }
34954 });/*
34955  * Based on:
34956  * Ext JS Library 1.1.1
34957  * Copyright(c) 2006-2007, Ext JS, LLC.
34958  *
34959  * Originally Released Under LGPL - original licence link has changed is not relivant.
34960  *
34961  * Fork - LGPL
34962  * <script type="text/javascript">
34963  */
34964  
34965 /**
34966  * @class Roo.bootstrap.layout.Region
34967  * @extends Roo.bootstrap.layout.Basic
34968  * This class represents a region in a layout manager.
34969  
34970  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34971  * @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})
34972  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34973  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34974  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34975  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34976  * @cfg {String}    title           The title for the region (overrides panel titles)
34977  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34978  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34979  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34980  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34981  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34982  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34983  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34984  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34985  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34986  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34987
34988  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34989  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34990  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34991  * @cfg {Number}    width           For East/West panels
34992  * @cfg {Number}    height          For North/South panels
34993  * @cfg {Boolean}   split           To show the splitter
34994  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34995  * 
34996  * @cfg {string}   cls             Extra CSS classes to add to region
34997  * 
34998  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34999  * @cfg {string}   region  the region that it inhabits..
35000  *
35001
35002  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35003  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35004
35005  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35006  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35007  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35008  */
35009 Roo.bootstrap.layout.Region = function(config)
35010 {
35011     this.applyConfig(config);
35012
35013     var mgr = config.mgr;
35014     var pos = config.region;
35015     config.skipConfig = true;
35016     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35017     
35018     if (mgr.el) {
35019         this.onRender(mgr.el);   
35020     }
35021      
35022     this.visible = true;
35023     this.collapsed = false;
35024     this.unrendered_panels = [];
35025 };
35026
35027 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35028
35029     position: '', // set by wrapper (eg. north/south etc..)
35030     unrendered_panels : null,  // unrendered panels.
35031     createBody : function(){
35032         /** This region's body element 
35033         * @type Roo.Element */
35034         this.bodyEl = this.el.createChild({
35035                 tag: "div",
35036                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35037         });
35038     },
35039
35040     onRender: function(ctr, pos)
35041     {
35042         var dh = Roo.DomHelper;
35043         /** This region's container element 
35044         * @type Roo.Element */
35045         this.el = dh.append(ctr.dom, {
35046                 tag: "div",
35047                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35048             }, true);
35049         /** This region's title element 
35050         * @type Roo.Element */
35051     
35052         this.titleEl = dh.append(this.el.dom,
35053             {
35054                     tag: "div",
35055                     unselectable: "on",
35056                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35057                     children:[
35058                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35059                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35060                     ]}, true);
35061         
35062         this.titleEl.enableDisplayMode();
35063         /** This region's title text element 
35064         * @type HTMLElement */
35065         this.titleTextEl = this.titleEl.dom.firstChild;
35066         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35067         /*
35068         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35069         this.closeBtn.enableDisplayMode();
35070         this.closeBtn.on("click", this.closeClicked, this);
35071         this.closeBtn.hide();
35072     */
35073         this.createBody(this.config);
35074         if(this.config.hideWhenEmpty){
35075             this.hide();
35076             this.on("paneladded", this.validateVisibility, this);
35077             this.on("panelremoved", this.validateVisibility, this);
35078         }
35079         if(this.autoScroll){
35080             this.bodyEl.setStyle("overflow", "auto");
35081         }else{
35082             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35083         }
35084         //if(c.titlebar !== false){
35085             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35086                 this.titleEl.hide();
35087             }else{
35088                 this.titleEl.show();
35089                 if(this.config.title){
35090                     this.titleTextEl.innerHTML = this.config.title;
35091                 }
35092             }
35093         //}
35094         if(this.config.collapsed){
35095             this.collapse(true);
35096         }
35097         if(this.config.hidden){
35098             this.hide();
35099         }
35100         
35101         if (this.unrendered_panels && this.unrendered_panels.length) {
35102             for (var i =0;i< this.unrendered_panels.length; i++) {
35103                 this.add(this.unrendered_panels[i]);
35104             }
35105             this.unrendered_panels = null;
35106             
35107         }
35108         
35109     },
35110     
35111     applyConfig : function(c)
35112     {
35113         /*
35114          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35115             var dh = Roo.DomHelper;
35116             if(c.titlebar !== false){
35117                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35118                 this.collapseBtn.on("click", this.collapse, this);
35119                 this.collapseBtn.enableDisplayMode();
35120                 /*
35121                 if(c.showPin === true || this.showPin){
35122                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35123                     this.stickBtn.enableDisplayMode();
35124                     this.stickBtn.on("click", this.expand, this);
35125                     this.stickBtn.hide();
35126                 }
35127                 
35128             }
35129             */
35130             /** This region's collapsed element
35131             * @type Roo.Element */
35132             /*
35133              *
35134             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35135                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35136             ]}, true);
35137             
35138             if(c.floatable !== false){
35139                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35140                this.collapsedEl.on("click", this.collapseClick, this);
35141             }
35142
35143             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35144                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35145                    id: "message", unselectable: "on", style:{"float":"left"}});
35146                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35147              }
35148             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35149             this.expandBtn.on("click", this.expand, this);
35150             
35151         }
35152         
35153         if(this.collapseBtn){
35154             this.collapseBtn.setVisible(c.collapsible == true);
35155         }
35156         
35157         this.cmargins = c.cmargins || this.cmargins ||
35158                          (this.position == "west" || this.position == "east" ?
35159                              {top: 0, left: 2, right:2, bottom: 0} :
35160                              {top: 2, left: 0, right:0, bottom: 2});
35161         */
35162         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35163         
35164         
35165         this.bottomTabs = c.tabPosition != "top";
35166         
35167         this.autoScroll = c.autoScroll || false;
35168         
35169         
35170        
35171         
35172         this.duration = c.duration || .30;
35173         this.slideDuration = c.slideDuration || .45;
35174         this.config = c;
35175        
35176     },
35177     /**
35178      * Returns true if this region is currently visible.
35179      * @return {Boolean}
35180      */
35181     isVisible : function(){
35182         return this.visible;
35183     },
35184
35185     /**
35186      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35187      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35188      */
35189     //setCollapsedTitle : function(title){
35190     //    title = title || "&#160;";
35191      //   if(this.collapsedTitleTextEl){
35192       //      this.collapsedTitleTextEl.innerHTML = title;
35193        // }
35194     //},
35195
35196     getBox : function(){
35197         var b;
35198       //  if(!this.collapsed){
35199             b = this.el.getBox(false, true);
35200        // }else{
35201           //  b = this.collapsedEl.getBox(false, true);
35202         //}
35203         return b;
35204     },
35205
35206     getMargins : function(){
35207         return this.margins;
35208         //return this.collapsed ? this.cmargins : this.margins;
35209     },
35210 /*
35211     highlight : function(){
35212         this.el.addClass("x-layout-panel-dragover");
35213     },
35214
35215     unhighlight : function(){
35216         this.el.removeClass("x-layout-panel-dragover");
35217     },
35218 */
35219     updateBox : function(box)
35220     {
35221         if (!this.bodyEl) {
35222             return; // not rendered yet..
35223         }
35224         
35225         this.box = box;
35226         if(!this.collapsed){
35227             this.el.dom.style.left = box.x + "px";
35228             this.el.dom.style.top = box.y + "px";
35229             this.updateBody(box.width, box.height);
35230         }else{
35231             this.collapsedEl.dom.style.left = box.x + "px";
35232             this.collapsedEl.dom.style.top = box.y + "px";
35233             this.collapsedEl.setSize(box.width, box.height);
35234         }
35235         if(this.tabs){
35236             this.tabs.autoSizeTabs();
35237         }
35238     },
35239
35240     updateBody : function(w, h)
35241     {
35242         if(w !== null){
35243             this.el.setWidth(w);
35244             w -= this.el.getBorderWidth("rl");
35245             if(this.config.adjustments){
35246                 w += this.config.adjustments[0];
35247             }
35248         }
35249         if(h !== null && h > 0){
35250             this.el.setHeight(h);
35251             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35252             h -= this.el.getBorderWidth("tb");
35253             if(this.config.adjustments){
35254                 h += this.config.adjustments[1];
35255             }
35256             this.bodyEl.setHeight(h);
35257             if(this.tabs){
35258                 h = this.tabs.syncHeight(h);
35259             }
35260         }
35261         if(this.panelSize){
35262             w = w !== null ? w : this.panelSize.width;
35263             h = h !== null ? h : this.panelSize.height;
35264         }
35265         if(this.activePanel){
35266             var el = this.activePanel.getEl();
35267             w = w !== null ? w : el.getWidth();
35268             h = h !== null ? h : el.getHeight();
35269             this.panelSize = {width: w, height: h};
35270             this.activePanel.setSize(w, h);
35271         }
35272         if(Roo.isIE && this.tabs){
35273             this.tabs.el.repaint();
35274         }
35275     },
35276
35277     /**
35278      * Returns the container element for this region.
35279      * @return {Roo.Element}
35280      */
35281     getEl : function(){
35282         return this.el;
35283     },
35284
35285     /**
35286      * Hides this region.
35287      */
35288     hide : function(){
35289         //if(!this.collapsed){
35290             this.el.dom.style.left = "-2000px";
35291             this.el.hide();
35292         //}else{
35293          //   this.collapsedEl.dom.style.left = "-2000px";
35294          //   this.collapsedEl.hide();
35295        // }
35296         this.visible = false;
35297         this.fireEvent("visibilitychange", this, false);
35298     },
35299
35300     /**
35301      * Shows this region if it was previously hidden.
35302      */
35303     show : function(){
35304         //if(!this.collapsed){
35305             this.el.show();
35306         //}else{
35307         //    this.collapsedEl.show();
35308        // }
35309         this.visible = true;
35310         this.fireEvent("visibilitychange", this, true);
35311     },
35312 /*
35313     closeClicked : function(){
35314         if(this.activePanel){
35315             this.remove(this.activePanel);
35316         }
35317     },
35318
35319     collapseClick : function(e){
35320         if(this.isSlid){
35321            e.stopPropagation();
35322            this.slideIn();
35323         }else{
35324            e.stopPropagation();
35325            this.slideOut();
35326         }
35327     },
35328 */
35329     /**
35330      * Collapses this region.
35331      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35332      */
35333     /*
35334     collapse : function(skipAnim, skipCheck = false){
35335         if(this.collapsed) {
35336             return;
35337         }
35338         
35339         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35340             
35341             this.collapsed = true;
35342             if(this.split){
35343                 this.split.el.hide();
35344             }
35345             if(this.config.animate && skipAnim !== true){
35346                 this.fireEvent("invalidated", this);
35347                 this.animateCollapse();
35348             }else{
35349                 this.el.setLocation(-20000,-20000);
35350                 this.el.hide();
35351                 this.collapsedEl.show();
35352                 this.fireEvent("collapsed", this);
35353                 this.fireEvent("invalidated", this);
35354             }
35355         }
35356         
35357     },
35358 */
35359     animateCollapse : function(){
35360         // overridden
35361     },
35362
35363     /**
35364      * Expands this region if it was previously collapsed.
35365      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35366      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35367      */
35368     /*
35369     expand : function(e, skipAnim){
35370         if(e) {
35371             e.stopPropagation();
35372         }
35373         if(!this.collapsed || this.el.hasActiveFx()) {
35374             return;
35375         }
35376         if(this.isSlid){
35377             this.afterSlideIn();
35378             skipAnim = true;
35379         }
35380         this.collapsed = false;
35381         if(this.config.animate && skipAnim !== true){
35382             this.animateExpand();
35383         }else{
35384             this.el.show();
35385             if(this.split){
35386                 this.split.el.show();
35387             }
35388             this.collapsedEl.setLocation(-2000,-2000);
35389             this.collapsedEl.hide();
35390             this.fireEvent("invalidated", this);
35391             this.fireEvent("expanded", this);
35392         }
35393     },
35394 */
35395     animateExpand : function(){
35396         // overridden
35397     },
35398
35399     initTabs : function()
35400     {
35401         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35402         
35403         var ts = new Roo.bootstrap.panel.Tabs({
35404                 el: this.bodyEl.dom,
35405                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35406                 disableTooltips: this.config.disableTabTips,
35407                 toolbar : this.config.toolbar
35408             });
35409         
35410         if(this.config.hideTabs){
35411             ts.stripWrap.setDisplayed(false);
35412         }
35413         this.tabs = ts;
35414         ts.resizeTabs = this.config.resizeTabs === true;
35415         ts.minTabWidth = this.config.minTabWidth || 40;
35416         ts.maxTabWidth = this.config.maxTabWidth || 250;
35417         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35418         ts.monitorResize = false;
35419         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35420         ts.bodyEl.addClass('roo-layout-tabs-body');
35421         this.panels.each(this.initPanelAsTab, this);
35422     },
35423
35424     initPanelAsTab : function(panel){
35425         var ti = this.tabs.addTab(
35426             panel.getEl().id,
35427             panel.getTitle(),
35428             null,
35429             this.config.closeOnTab && panel.isClosable(),
35430             panel.tpl
35431         );
35432         if(panel.tabTip !== undefined){
35433             ti.setTooltip(panel.tabTip);
35434         }
35435         ti.on("activate", function(){
35436               this.setActivePanel(panel);
35437         }, this);
35438         
35439         if(this.config.closeOnTab){
35440             ti.on("beforeclose", function(t, e){
35441                 e.cancel = true;
35442                 this.remove(panel);
35443             }, this);
35444         }
35445         
35446         panel.tabItem = ti;
35447         
35448         return ti;
35449     },
35450
35451     updatePanelTitle : function(panel, title)
35452     {
35453         if(this.activePanel == panel){
35454             this.updateTitle(title);
35455         }
35456         if(this.tabs){
35457             var ti = this.tabs.getTab(panel.getEl().id);
35458             ti.setText(title);
35459             if(panel.tabTip !== undefined){
35460                 ti.setTooltip(panel.tabTip);
35461             }
35462         }
35463     },
35464
35465     updateTitle : function(title){
35466         if(this.titleTextEl && !this.config.title){
35467             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35468         }
35469     },
35470
35471     setActivePanel : function(panel)
35472     {
35473         panel = this.getPanel(panel);
35474         if(this.activePanel && this.activePanel != panel){
35475             if(this.activePanel.setActiveState(false) === false){
35476                 return;
35477             }
35478         }
35479         this.activePanel = panel;
35480         panel.setActiveState(true);
35481         if(this.panelSize){
35482             panel.setSize(this.panelSize.width, this.panelSize.height);
35483         }
35484         if(this.closeBtn){
35485             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35486         }
35487         this.updateTitle(panel.getTitle());
35488         if(this.tabs){
35489             this.fireEvent("invalidated", this);
35490         }
35491         this.fireEvent("panelactivated", this, panel);
35492     },
35493
35494     /**
35495      * Shows the specified panel.
35496      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35497      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35498      */
35499     showPanel : function(panel)
35500     {
35501         panel = this.getPanel(panel);
35502         if(panel){
35503             if(this.tabs){
35504                 var tab = this.tabs.getTab(panel.getEl().id);
35505                 if(tab.isHidden()){
35506                     this.tabs.unhideTab(tab.id);
35507                 }
35508                 tab.activate();
35509             }else{
35510                 this.setActivePanel(panel);
35511             }
35512         }
35513         return panel;
35514     },
35515
35516     /**
35517      * Get the active panel for this region.
35518      * @return {Roo.ContentPanel} The active panel or null
35519      */
35520     getActivePanel : function(){
35521         return this.activePanel;
35522     },
35523
35524     validateVisibility : function(){
35525         if(this.panels.getCount() < 1){
35526             this.updateTitle("&#160;");
35527             this.closeBtn.hide();
35528             this.hide();
35529         }else{
35530             if(!this.isVisible()){
35531                 this.show();
35532             }
35533         }
35534     },
35535
35536     /**
35537      * Adds the passed ContentPanel(s) to this region.
35538      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35539      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35540      */
35541     add : function(panel)
35542     {
35543         if(arguments.length > 1){
35544             for(var i = 0, len = arguments.length; i < len; i++) {
35545                 this.add(arguments[i]);
35546             }
35547             return null;
35548         }
35549         
35550         // if we have not been rendered yet, then we can not really do much of this..
35551         if (!this.bodyEl) {
35552             this.unrendered_panels.push(panel);
35553             return panel;
35554         }
35555         
35556         
35557         
35558         
35559         if(this.hasPanel(panel)){
35560             this.showPanel(panel);
35561             return panel;
35562         }
35563         panel.setRegion(this);
35564         this.panels.add(panel);
35565        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35566             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35567             // and hide them... ???
35568             this.bodyEl.dom.appendChild(panel.getEl().dom);
35569             if(panel.background !== true){
35570                 this.setActivePanel(panel);
35571             }
35572             this.fireEvent("paneladded", this, panel);
35573             return panel;
35574         }
35575         */
35576         if(!this.tabs){
35577             this.initTabs();
35578         }else{
35579             this.initPanelAsTab(panel);
35580         }
35581         
35582         
35583         if(panel.background !== true){
35584             this.tabs.activate(panel.getEl().id);
35585         }
35586         this.fireEvent("paneladded", this, panel);
35587         return panel;
35588     },
35589
35590     /**
35591      * Hides the tab for the specified panel.
35592      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35593      */
35594     hidePanel : function(panel){
35595         if(this.tabs && (panel = this.getPanel(panel))){
35596             this.tabs.hideTab(panel.getEl().id);
35597         }
35598     },
35599
35600     /**
35601      * Unhides the tab for a previously hidden panel.
35602      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35603      */
35604     unhidePanel : function(panel){
35605         if(this.tabs && (panel = this.getPanel(panel))){
35606             this.tabs.unhideTab(panel.getEl().id);
35607         }
35608     },
35609
35610     clearPanels : function(){
35611         while(this.panels.getCount() > 0){
35612              this.remove(this.panels.first());
35613         }
35614     },
35615
35616     /**
35617      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35618      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35619      * @param {Boolean} preservePanel Overrides the config preservePanel option
35620      * @return {Roo.ContentPanel} The panel that was removed
35621      */
35622     remove : function(panel, preservePanel)
35623     {
35624         panel = this.getPanel(panel);
35625         if(!panel){
35626             return null;
35627         }
35628         var e = {};
35629         this.fireEvent("beforeremove", this, panel, e);
35630         if(e.cancel === true){
35631             return null;
35632         }
35633         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35634         var panelId = panel.getId();
35635         this.panels.removeKey(panelId);
35636         if(preservePanel){
35637             document.body.appendChild(panel.getEl().dom);
35638         }
35639         if(this.tabs){
35640             this.tabs.removeTab(panel.getEl().id);
35641         }else if (!preservePanel){
35642             this.bodyEl.dom.removeChild(panel.getEl().dom);
35643         }
35644         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35645             var p = this.panels.first();
35646             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35647             tempEl.appendChild(p.getEl().dom);
35648             this.bodyEl.update("");
35649             this.bodyEl.dom.appendChild(p.getEl().dom);
35650             tempEl = null;
35651             this.updateTitle(p.getTitle());
35652             this.tabs = null;
35653             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35654             this.setActivePanel(p);
35655         }
35656         panel.setRegion(null);
35657         if(this.activePanel == panel){
35658             this.activePanel = null;
35659         }
35660         if(this.config.autoDestroy !== false && preservePanel !== true){
35661             try{panel.destroy();}catch(e){}
35662         }
35663         this.fireEvent("panelremoved", this, panel);
35664         return panel;
35665     },
35666
35667     /**
35668      * Returns the TabPanel component used by this region
35669      * @return {Roo.TabPanel}
35670      */
35671     getTabs : function(){
35672         return this.tabs;
35673     },
35674
35675     createTool : function(parentEl, className){
35676         var btn = Roo.DomHelper.append(parentEl, {
35677             tag: "div",
35678             cls: "x-layout-tools-button",
35679             children: [ {
35680                 tag: "div",
35681                 cls: "roo-layout-tools-button-inner " + className,
35682                 html: "&#160;"
35683             }]
35684         }, true);
35685         btn.addClassOnOver("roo-layout-tools-button-over");
35686         return btn;
35687     }
35688 });/*
35689  * Based on:
35690  * Ext JS Library 1.1.1
35691  * Copyright(c) 2006-2007, Ext JS, LLC.
35692  *
35693  * Originally Released Under LGPL - original licence link has changed is not relivant.
35694  *
35695  * Fork - LGPL
35696  * <script type="text/javascript">
35697  */
35698  
35699
35700
35701 /**
35702  * @class Roo.SplitLayoutRegion
35703  * @extends Roo.LayoutRegion
35704  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35705  */
35706 Roo.bootstrap.layout.Split = function(config){
35707     this.cursor = config.cursor;
35708     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35709 };
35710
35711 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35712 {
35713     splitTip : "Drag to resize.",
35714     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35715     useSplitTips : false,
35716
35717     applyConfig : function(config){
35718         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35719     },
35720     
35721     onRender : function(ctr,pos) {
35722         
35723         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35724         if(!this.config.split){
35725             return;
35726         }
35727         if(!this.split){
35728             
35729             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35730                             tag: "div",
35731                             id: this.el.id + "-split",
35732                             cls: "roo-layout-split roo-layout-split-"+this.position,
35733                             html: "&#160;"
35734             });
35735             /** The SplitBar for this region 
35736             * @type Roo.SplitBar */
35737             // does not exist yet...
35738             Roo.log([this.position, this.orientation]);
35739             
35740             this.split = new Roo.bootstrap.SplitBar({
35741                 dragElement : splitEl,
35742                 resizingElement: this.el,
35743                 orientation : this.orientation
35744             });
35745             
35746             this.split.on("moved", this.onSplitMove, this);
35747             this.split.useShim = this.config.useShim === true;
35748             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35749             if(this.useSplitTips){
35750                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35751             }
35752             //if(config.collapsible){
35753             //    this.split.el.on("dblclick", this.collapse,  this);
35754             //}
35755         }
35756         if(typeof this.config.minSize != "undefined"){
35757             this.split.minSize = this.config.minSize;
35758         }
35759         if(typeof this.config.maxSize != "undefined"){
35760             this.split.maxSize = this.config.maxSize;
35761         }
35762         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35763             this.hideSplitter();
35764         }
35765         
35766     },
35767
35768     getHMaxSize : function(){
35769          var cmax = this.config.maxSize || 10000;
35770          var center = this.mgr.getRegion("center");
35771          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35772     },
35773
35774     getVMaxSize : function(){
35775          var cmax = this.config.maxSize || 10000;
35776          var center = this.mgr.getRegion("center");
35777          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35778     },
35779
35780     onSplitMove : function(split, newSize){
35781         this.fireEvent("resized", this, newSize);
35782     },
35783     
35784     /** 
35785      * Returns the {@link Roo.SplitBar} for this region.
35786      * @return {Roo.SplitBar}
35787      */
35788     getSplitBar : function(){
35789         return this.split;
35790     },
35791     
35792     hide : function(){
35793         this.hideSplitter();
35794         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35795     },
35796
35797     hideSplitter : function(){
35798         if(this.split){
35799             this.split.el.setLocation(-2000,-2000);
35800             this.split.el.hide();
35801         }
35802     },
35803
35804     show : function(){
35805         if(this.split){
35806             this.split.el.show();
35807         }
35808         Roo.bootstrap.layout.Split.superclass.show.call(this);
35809     },
35810     
35811     beforeSlide: function(){
35812         if(Roo.isGecko){// firefox overflow auto bug workaround
35813             this.bodyEl.clip();
35814             if(this.tabs) {
35815                 this.tabs.bodyEl.clip();
35816             }
35817             if(this.activePanel){
35818                 this.activePanel.getEl().clip();
35819                 
35820                 if(this.activePanel.beforeSlide){
35821                     this.activePanel.beforeSlide();
35822                 }
35823             }
35824         }
35825     },
35826     
35827     afterSlide : function(){
35828         if(Roo.isGecko){// firefox overflow auto bug workaround
35829             this.bodyEl.unclip();
35830             if(this.tabs) {
35831                 this.tabs.bodyEl.unclip();
35832             }
35833             if(this.activePanel){
35834                 this.activePanel.getEl().unclip();
35835                 if(this.activePanel.afterSlide){
35836                     this.activePanel.afterSlide();
35837                 }
35838             }
35839         }
35840     },
35841
35842     initAutoHide : function(){
35843         if(this.autoHide !== false){
35844             if(!this.autoHideHd){
35845                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35846                 this.autoHideHd = {
35847                     "mouseout": function(e){
35848                         if(!e.within(this.el, true)){
35849                             st.delay(500);
35850                         }
35851                     },
35852                     "mouseover" : function(e){
35853                         st.cancel();
35854                     },
35855                     scope : this
35856                 };
35857             }
35858             this.el.on(this.autoHideHd);
35859         }
35860     },
35861
35862     clearAutoHide : function(){
35863         if(this.autoHide !== false){
35864             this.el.un("mouseout", this.autoHideHd.mouseout);
35865             this.el.un("mouseover", this.autoHideHd.mouseover);
35866         }
35867     },
35868
35869     clearMonitor : function(){
35870         Roo.get(document).un("click", this.slideInIf, this);
35871     },
35872
35873     // these names are backwards but not changed for compat
35874     slideOut : function(){
35875         if(this.isSlid || this.el.hasActiveFx()){
35876             return;
35877         }
35878         this.isSlid = true;
35879         if(this.collapseBtn){
35880             this.collapseBtn.hide();
35881         }
35882         this.closeBtnState = this.closeBtn.getStyle('display');
35883         this.closeBtn.hide();
35884         if(this.stickBtn){
35885             this.stickBtn.show();
35886         }
35887         this.el.show();
35888         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35889         this.beforeSlide();
35890         this.el.setStyle("z-index", 10001);
35891         this.el.slideIn(this.getSlideAnchor(), {
35892             callback: function(){
35893                 this.afterSlide();
35894                 this.initAutoHide();
35895                 Roo.get(document).on("click", this.slideInIf, this);
35896                 this.fireEvent("slideshow", this);
35897             },
35898             scope: this,
35899             block: true
35900         });
35901     },
35902
35903     afterSlideIn : function(){
35904         this.clearAutoHide();
35905         this.isSlid = false;
35906         this.clearMonitor();
35907         this.el.setStyle("z-index", "");
35908         if(this.collapseBtn){
35909             this.collapseBtn.show();
35910         }
35911         this.closeBtn.setStyle('display', this.closeBtnState);
35912         if(this.stickBtn){
35913             this.stickBtn.hide();
35914         }
35915         this.fireEvent("slidehide", this);
35916     },
35917
35918     slideIn : function(cb){
35919         if(!this.isSlid || this.el.hasActiveFx()){
35920             Roo.callback(cb);
35921             return;
35922         }
35923         this.isSlid = false;
35924         this.beforeSlide();
35925         this.el.slideOut(this.getSlideAnchor(), {
35926             callback: function(){
35927                 this.el.setLeftTop(-10000, -10000);
35928                 this.afterSlide();
35929                 this.afterSlideIn();
35930                 Roo.callback(cb);
35931             },
35932             scope: this,
35933             block: true
35934         });
35935     },
35936     
35937     slideInIf : function(e){
35938         if(!e.within(this.el)){
35939             this.slideIn();
35940         }
35941     },
35942
35943     animateCollapse : function(){
35944         this.beforeSlide();
35945         this.el.setStyle("z-index", 20000);
35946         var anchor = this.getSlideAnchor();
35947         this.el.slideOut(anchor, {
35948             callback : function(){
35949                 this.el.setStyle("z-index", "");
35950                 this.collapsedEl.slideIn(anchor, {duration:.3});
35951                 this.afterSlide();
35952                 this.el.setLocation(-10000,-10000);
35953                 this.el.hide();
35954                 this.fireEvent("collapsed", this);
35955             },
35956             scope: this,
35957             block: true
35958         });
35959     },
35960
35961     animateExpand : function(){
35962         this.beforeSlide();
35963         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35964         this.el.setStyle("z-index", 20000);
35965         this.collapsedEl.hide({
35966             duration:.1
35967         });
35968         this.el.slideIn(this.getSlideAnchor(), {
35969             callback : function(){
35970                 this.el.setStyle("z-index", "");
35971                 this.afterSlide();
35972                 if(this.split){
35973                     this.split.el.show();
35974                 }
35975                 this.fireEvent("invalidated", this);
35976                 this.fireEvent("expanded", this);
35977             },
35978             scope: this,
35979             block: true
35980         });
35981     },
35982
35983     anchors : {
35984         "west" : "left",
35985         "east" : "right",
35986         "north" : "top",
35987         "south" : "bottom"
35988     },
35989
35990     sanchors : {
35991         "west" : "l",
35992         "east" : "r",
35993         "north" : "t",
35994         "south" : "b"
35995     },
35996
35997     canchors : {
35998         "west" : "tl-tr",
35999         "east" : "tr-tl",
36000         "north" : "tl-bl",
36001         "south" : "bl-tl"
36002     },
36003
36004     getAnchor : function(){
36005         return this.anchors[this.position];
36006     },
36007
36008     getCollapseAnchor : function(){
36009         return this.canchors[this.position];
36010     },
36011
36012     getSlideAnchor : function(){
36013         return this.sanchors[this.position];
36014     },
36015
36016     getAlignAdj : function(){
36017         var cm = this.cmargins;
36018         switch(this.position){
36019             case "west":
36020                 return [0, 0];
36021             break;
36022             case "east":
36023                 return [0, 0];
36024             break;
36025             case "north":
36026                 return [0, 0];
36027             break;
36028             case "south":
36029                 return [0, 0];
36030             break;
36031         }
36032     },
36033
36034     getExpandAdj : function(){
36035         var c = this.collapsedEl, cm = this.cmargins;
36036         switch(this.position){
36037             case "west":
36038                 return [-(cm.right+c.getWidth()+cm.left), 0];
36039             break;
36040             case "east":
36041                 return [cm.right+c.getWidth()+cm.left, 0];
36042             break;
36043             case "north":
36044                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36045             break;
36046             case "south":
36047                 return [0, cm.top+cm.bottom+c.getHeight()];
36048             break;
36049         }
36050     }
36051 });/*
36052  * Based on:
36053  * Ext JS Library 1.1.1
36054  * Copyright(c) 2006-2007, Ext JS, LLC.
36055  *
36056  * Originally Released Under LGPL - original licence link has changed is not relivant.
36057  *
36058  * Fork - LGPL
36059  * <script type="text/javascript">
36060  */
36061 /*
36062  * These classes are private internal classes
36063  */
36064 Roo.bootstrap.layout.Center = function(config){
36065     config.region = "center";
36066     Roo.bootstrap.layout.Region.call(this, config);
36067     this.visible = true;
36068     this.minWidth = config.minWidth || 20;
36069     this.minHeight = config.minHeight || 20;
36070 };
36071
36072 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36073     hide : function(){
36074         // center panel can't be hidden
36075     },
36076     
36077     show : function(){
36078         // center panel can't be hidden
36079     },
36080     
36081     getMinWidth: function(){
36082         return this.minWidth;
36083     },
36084     
36085     getMinHeight: function(){
36086         return this.minHeight;
36087     }
36088 });
36089
36090
36091
36092
36093  
36094
36095
36096
36097
36098
36099 Roo.bootstrap.layout.North = function(config)
36100 {
36101     config.region = 'north';
36102     config.cursor = 'n-resize';
36103     
36104     Roo.bootstrap.layout.Split.call(this, config);
36105     
36106     
36107     if(this.split){
36108         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36109         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36110         this.split.el.addClass("roo-layout-split-v");
36111     }
36112     var size = config.initialSize || config.height;
36113     if(typeof size != "undefined"){
36114         this.el.setHeight(size);
36115     }
36116 };
36117 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36118 {
36119     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36120     
36121     
36122     
36123     getBox : function(){
36124         if(this.collapsed){
36125             return this.collapsedEl.getBox();
36126         }
36127         var box = this.el.getBox();
36128         if(this.split){
36129             box.height += this.split.el.getHeight();
36130         }
36131         return box;
36132     },
36133     
36134     updateBox : function(box){
36135         if(this.split && !this.collapsed){
36136             box.height -= this.split.el.getHeight();
36137             this.split.el.setLeft(box.x);
36138             this.split.el.setTop(box.y+box.height);
36139             this.split.el.setWidth(box.width);
36140         }
36141         if(this.collapsed){
36142             this.updateBody(box.width, null);
36143         }
36144         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36145     }
36146 });
36147
36148
36149
36150
36151
36152 Roo.bootstrap.layout.South = function(config){
36153     config.region = 'south';
36154     config.cursor = 's-resize';
36155     Roo.bootstrap.layout.Split.call(this, config);
36156     if(this.split){
36157         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36158         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36159         this.split.el.addClass("roo-layout-split-v");
36160     }
36161     var size = config.initialSize || config.height;
36162     if(typeof size != "undefined"){
36163         this.el.setHeight(size);
36164     }
36165 };
36166
36167 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36168     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36169     getBox : function(){
36170         if(this.collapsed){
36171             return this.collapsedEl.getBox();
36172         }
36173         var box = this.el.getBox();
36174         if(this.split){
36175             var sh = this.split.el.getHeight();
36176             box.height += sh;
36177             box.y -= sh;
36178         }
36179         return box;
36180     },
36181     
36182     updateBox : function(box){
36183         if(this.split && !this.collapsed){
36184             var sh = this.split.el.getHeight();
36185             box.height -= sh;
36186             box.y += sh;
36187             this.split.el.setLeft(box.x);
36188             this.split.el.setTop(box.y-sh);
36189             this.split.el.setWidth(box.width);
36190         }
36191         if(this.collapsed){
36192             this.updateBody(box.width, null);
36193         }
36194         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36195     }
36196 });
36197
36198 Roo.bootstrap.layout.East = function(config){
36199     config.region = "east";
36200     config.cursor = "e-resize";
36201     Roo.bootstrap.layout.Split.call(this, config);
36202     if(this.split){
36203         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36204         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36205         this.split.el.addClass("roo-layout-split-h");
36206     }
36207     var size = config.initialSize || config.width;
36208     if(typeof size != "undefined"){
36209         this.el.setWidth(size);
36210     }
36211 };
36212 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36213     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36214     getBox : function(){
36215         if(this.collapsed){
36216             return this.collapsedEl.getBox();
36217         }
36218         var box = this.el.getBox();
36219         if(this.split){
36220             var sw = this.split.el.getWidth();
36221             box.width += sw;
36222             box.x -= sw;
36223         }
36224         return box;
36225     },
36226
36227     updateBox : function(box){
36228         if(this.split && !this.collapsed){
36229             var sw = this.split.el.getWidth();
36230             box.width -= sw;
36231             this.split.el.setLeft(box.x);
36232             this.split.el.setTop(box.y);
36233             this.split.el.setHeight(box.height);
36234             box.x += sw;
36235         }
36236         if(this.collapsed){
36237             this.updateBody(null, box.height);
36238         }
36239         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36240     }
36241 });
36242
36243 Roo.bootstrap.layout.West = function(config){
36244     config.region = "west";
36245     config.cursor = "w-resize";
36246     
36247     Roo.bootstrap.layout.Split.call(this, config);
36248     if(this.split){
36249         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36250         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36251         this.split.el.addClass("roo-layout-split-h");
36252     }
36253     
36254 };
36255 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36256     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36257     
36258     onRender: function(ctr, pos)
36259     {
36260         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36261         var size = this.config.initialSize || this.config.width;
36262         if(typeof size != "undefined"){
36263             this.el.setWidth(size);
36264         }
36265     },
36266     
36267     getBox : function(){
36268         if(this.collapsed){
36269             return this.collapsedEl.getBox();
36270         }
36271         var box = this.el.getBox();
36272         if(this.split){
36273             box.width += this.split.el.getWidth();
36274         }
36275         return box;
36276     },
36277     
36278     updateBox : function(box){
36279         if(this.split && !this.collapsed){
36280             var sw = this.split.el.getWidth();
36281             box.width -= sw;
36282             this.split.el.setLeft(box.x+box.width);
36283             this.split.el.setTop(box.y);
36284             this.split.el.setHeight(box.height);
36285         }
36286         if(this.collapsed){
36287             this.updateBody(null, box.height);
36288         }
36289         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36290     }
36291 });
36292 Roo.namespace("Roo.bootstrap.panel");/*
36293  * Based on:
36294  * Ext JS Library 1.1.1
36295  * Copyright(c) 2006-2007, Ext JS, LLC.
36296  *
36297  * Originally Released Under LGPL - original licence link has changed is not relivant.
36298  *
36299  * Fork - LGPL
36300  * <script type="text/javascript">
36301  */
36302 /**
36303  * @class Roo.ContentPanel
36304  * @extends Roo.util.Observable
36305  * A basic ContentPanel element.
36306  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36307  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36308  * @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
36309  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36310  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36311  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36312  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36313  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36314  * @cfg {String} title          The title for this panel
36315  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36316  * @cfg {String} url            Calls {@link #setUrl} with this value
36317  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36318  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36319  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36320  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36321  * @cfg {Boolean} badges render the badges
36322
36323  * @constructor
36324  * Create a new ContentPanel.
36325  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36326  * @param {String/Object} config A string to set only the title or a config object
36327  * @param {String} content (optional) Set the HTML content for this panel
36328  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36329  */
36330 Roo.bootstrap.panel.Content = function( config){
36331     
36332     this.tpl = config.tpl || false;
36333     
36334     var el = config.el;
36335     var content = config.content;
36336
36337     if(config.autoCreate){ // xtype is available if this is called from factory
36338         el = Roo.id();
36339     }
36340     this.el = Roo.get(el);
36341     if(!this.el && config && config.autoCreate){
36342         if(typeof config.autoCreate == "object"){
36343             if(!config.autoCreate.id){
36344                 config.autoCreate.id = config.id||el;
36345             }
36346             this.el = Roo.DomHelper.append(document.body,
36347                         config.autoCreate, true);
36348         }else{
36349             var elcfg =  {   tag: "div",
36350                             cls: "roo-layout-inactive-content",
36351                             id: config.id||el
36352                             };
36353             if (config.html) {
36354                 elcfg.html = config.html;
36355                 
36356             }
36357                         
36358             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36359         }
36360     } 
36361     this.closable = false;
36362     this.loaded = false;
36363     this.active = false;
36364    
36365       
36366     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36367         
36368         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36369         
36370         this.wrapEl = this.el; //this.el.wrap();
36371         var ti = [];
36372         if (config.toolbar.items) {
36373             ti = config.toolbar.items ;
36374             delete config.toolbar.items ;
36375         }
36376         
36377         var nitems = [];
36378         this.toolbar.render(this.wrapEl, 'before');
36379         for(var i =0;i < ti.length;i++) {
36380           //  Roo.log(['add child', items[i]]);
36381             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36382         }
36383         this.toolbar.items = nitems;
36384         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36385         delete config.toolbar;
36386         
36387     }
36388     /*
36389     // xtype created footer. - not sure if will work as we normally have to render first..
36390     if (this.footer && !this.footer.el && this.footer.xtype) {
36391         if (!this.wrapEl) {
36392             this.wrapEl = this.el.wrap();
36393         }
36394     
36395         this.footer.container = this.wrapEl.createChild();
36396          
36397         this.footer = Roo.factory(this.footer, Roo);
36398         
36399     }
36400     */
36401     
36402      if(typeof config == "string"){
36403         this.title = config;
36404     }else{
36405         Roo.apply(this, config);
36406     }
36407     
36408     if(this.resizeEl){
36409         this.resizeEl = Roo.get(this.resizeEl, true);
36410     }else{
36411         this.resizeEl = this.el;
36412     }
36413     // handle view.xtype
36414     
36415  
36416     
36417     
36418     this.addEvents({
36419         /**
36420          * @event activate
36421          * Fires when this panel is activated. 
36422          * @param {Roo.ContentPanel} this
36423          */
36424         "activate" : true,
36425         /**
36426          * @event deactivate
36427          * Fires when this panel is activated. 
36428          * @param {Roo.ContentPanel} this
36429          */
36430         "deactivate" : true,
36431
36432         /**
36433          * @event resize
36434          * Fires when this panel is resized if fitToFrame is true.
36435          * @param {Roo.ContentPanel} this
36436          * @param {Number} width The width after any component adjustments
36437          * @param {Number} height The height after any component adjustments
36438          */
36439         "resize" : true,
36440         
36441          /**
36442          * @event render
36443          * Fires when this tab is created
36444          * @param {Roo.ContentPanel} this
36445          */
36446         "render" : true
36447         
36448         
36449         
36450     });
36451     
36452
36453     
36454     
36455     if(this.autoScroll){
36456         this.resizeEl.setStyle("overflow", "auto");
36457     } else {
36458         // fix randome scrolling
36459         //this.el.on('scroll', function() {
36460         //    Roo.log('fix random scolling');
36461         //    this.scrollTo('top',0); 
36462         //});
36463     }
36464     content = content || this.content;
36465     if(content){
36466         this.setContent(content);
36467     }
36468     if(config && config.url){
36469         this.setUrl(this.url, this.params, this.loadOnce);
36470     }
36471     
36472     
36473     
36474     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36475     
36476     if (this.view && typeof(this.view.xtype) != 'undefined') {
36477         this.view.el = this.el.appendChild(document.createElement("div"));
36478         this.view = Roo.factory(this.view); 
36479         this.view.render  &&  this.view.render(false, '');  
36480     }
36481     
36482     
36483     this.fireEvent('render', this);
36484 };
36485
36486 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36487     
36488     tabTip : '',
36489     
36490     setRegion : function(region){
36491         this.region = region;
36492         this.setActiveClass(region && !this.background);
36493     },
36494     
36495     
36496     setActiveClass: function(state)
36497     {
36498         if(state){
36499            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36500            this.el.setStyle('position','relative');
36501         }else{
36502            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36503            this.el.setStyle('position', 'absolute');
36504         } 
36505     },
36506     
36507     /**
36508      * Returns the toolbar for this Panel if one was configured. 
36509      * @return {Roo.Toolbar} 
36510      */
36511     getToolbar : function(){
36512         return this.toolbar;
36513     },
36514     
36515     setActiveState : function(active)
36516     {
36517         this.active = active;
36518         this.setActiveClass(active);
36519         if(!active){
36520             if(this.fireEvent("deactivate", this) === false){
36521                 return false;
36522             }
36523             return true;
36524         }
36525         this.fireEvent("activate", this);
36526         return true;
36527     },
36528     /**
36529      * Updates this panel's element
36530      * @param {String} content The new content
36531      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36532     */
36533     setContent : function(content, loadScripts){
36534         this.el.update(content, loadScripts);
36535     },
36536
36537     ignoreResize : function(w, h){
36538         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36539             return true;
36540         }else{
36541             this.lastSize = {width: w, height: h};
36542             return false;
36543         }
36544     },
36545     /**
36546      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36547      * @return {Roo.UpdateManager} The UpdateManager
36548      */
36549     getUpdateManager : function(){
36550         return this.el.getUpdateManager();
36551     },
36552      /**
36553      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36554      * @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:
36555 <pre><code>
36556 panel.load({
36557     url: "your-url.php",
36558     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36559     callback: yourFunction,
36560     scope: yourObject, //(optional scope)
36561     discardUrl: false,
36562     nocache: false,
36563     text: "Loading...",
36564     timeout: 30,
36565     scripts: false
36566 });
36567 </code></pre>
36568      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36569      * 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.
36570      * @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}
36571      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36572      * @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.
36573      * @return {Roo.ContentPanel} this
36574      */
36575     load : function(){
36576         var um = this.el.getUpdateManager();
36577         um.update.apply(um, arguments);
36578         return this;
36579     },
36580
36581
36582     /**
36583      * 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.
36584      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36585      * @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)
36586      * @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)
36587      * @return {Roo.UpdateManager} The UpdateManager
36588      */
36589     setUrl : function(url, params, loadOnce){
36590         if(this.refreshDelegate){
36591             this.removeListener("activate", this.refreshDelegate);
36592         }
36593         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36594         this.on("activate", this.refreshDelegate);
36595         return this.el.getUpdateManager();
36596     },
36597     
36598     _handleRefresh : function(url, params, loadOnce){
36599         if(!loadOnce || !this.loaded){
36600             var updater = this.el.getUpdateManager();
36601             updater.update(url, params, this._setLoaded.createDelegate(this));
36602         }
36603     },
36604     
36605     _setLoaded : function(){
36606         this.loaded = true;
36607     }, 
36608     
36609     /**
36610      * Returns this panel's id
36611      * @return {String} 
36612      */
36613     getId : function(){
36614         return this.el.id;
36615     },
36616     
36617     /** 
36618      * Returns this panel's element - used by regiosn to add.
36619      * @return {Roo.Element} 
36620      */
36621     getEl : function(){
36622         return this.wrapEl || this.el;
36623     },
36624     
36625    
36626     
36627     adjustForComponents : function(width, height)
36628     {
36629         //Roo.log('adjustForComponents ');
36630         if(this.resizeEl != this.el){
36631             width -= this.el.getFrameWidth('lr');
36632             height -= this.el.getFrameWidth('tb');
36633         }
36634         if(this.toolbar){
36635             var te = this.toolbar.getEl();
36636             te.setWidth(width);
36637             height -= te.getHeight();
36638         }
36639         if(this.footer){
36640             var te = this.footer.getEl();
36641             te.setWidth(width);
36642             height -= te.getHeight();
36643         }
36644         
36645         
36646         if(this.adjustments){
36647             width += this.adjustments[0];
36648             height += this.adjustments[1];
36649         }
36650         return {"width": width, "height": height};
36651     },
36652     
36653     setSize : function(width, height){
36654         if(this.fitToFrame && !this.ignoreResize(width, height)){
36655             if(this.fitContainer && this.resizeEl != this.el){
36656                 this.el.setSize(width, height);
36657             }
36658             var size = this.adjustForComponents(width, height);
36659             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36660             this.fireEvent('resize', this, size.width, size.height);
36661         }
36662     },
36663     
36664     /**
36665      * Returns this panel's title
36666      * @return {String} 
36667      */
36668     getTitle : function(){
36669         
36670         if (typeof(this.title) != 'object') {
36671             return this.title;
36672         }
36673         
36674         var t = '';
36675         for (var k in this.title) {
36676             if (!this.title.hasOwnProperty(k)) {
36677                 continue;
36678             }
36679             
36680             if (k.indexOf('-') >= 0) {
36681                 var s = k.split('-');
36682                 for (var i = 0; i<s.length; i++) {
36683                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36684                 }
36685             } else {
36686                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36687             }
36688         }
36689         return t;
36690     },
36691     
36692     /**
36693      * Set this panel's title
36694      * @param {String} title
36695      */
36696     setTitle : function(title){
36697         this.title = title;
36698         if(this.region){
36699             this.region.updatePanelTitle(this, title);
36700         }
36701     },
36702     
36703     /**
36704      * Returns true is this panel was configured to be closable
36705      * @return {Boolean} 
36706      */
36707     isClosable : function(){
36708         return this.closable;
36709     },
36710     
36711     beforeSlide : function(){
36712         this.el.clip();
36713         this.resizeEl.clip();
36714     },
36715     
36716     afterSlide : function(){
36717         this.el.unclip();
36718         this.resizeEl.unclip();
36719     },
36720     
36721     /**
36722      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36723      *   Will fail silently if the {@link #setUrl} method has not been called.
36724      *   This does not activate the panel, just updates its content.
36725      */
36726     refresh : function(){
36727         if(this.refreshDelegate){
36728            this.loaded = false;
36729            this.refreshDelegate();
36730         }
36731     },
36732     
36733     /**
36734      * Destroys this panel
36735      */
36736     destroy : function(){
36737         this.el.removeAllListeners();
36738         var tempEl = document.createElement("span");
36739         tempEl.appendChild(this.el.dom);
36740         tempEl.innerHTML = "";
36741         this.el.remove();
36742         this.el = null;
36743     },
36744     
36745     /**
36746      * form - if the content panel contains a form - this is a reference to it.
36747      * @type {Roo.form.Form}
36748      */
36749     form : false,
36750     /**
36751      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36752      *    This contains a reference to it.
36753      * @type {Roo.View}
36754      */
36755     view : false,
36756     
36757       /**
36758      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36759      * <pre><code>
36760
36761 layout.addxtype({
36762        xtype : 'Form',
36763        items: [ .... ]
36764    }
36765 );
36766
36767 </code></pre>
36768      * @param {Object} cfg Xtype definition of item to add.
36769      */
36770     
36771     
36772     getChildContainer: function () {
36773         return this.getEl();
36774     }
36775     
36776     
36777     /*
36778         var  ret = new Roo.factory(cfg);
36779         return ret;
36780         
36781         
36782         // add form..
36783         if (cfg.xtype.match(/^Form$/)) {
36784             
36785             var el;
36786             //if (this.footer) {
36787             //    el = this.footer.container.insertSibling(false, 'before');
36788             //} else {
36789                 el = this.el.createChild();
36790             //}
36791
36792             this.form = new  Roo.form.Form(cfg);
36793             
36794             
36795             if ( this.form.allItems.length) {
36796                 this.form.render(el.dom);
36797             }
36798             return this.form;
36799         }
36800         // should only have one of theses..
36801         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36802             // views.. should not be just added - used named prop 'view''
36803             
36804             cfg.el = this.el.appendChild(document.createElement("div"));
36805             // factory?
36806             
36807             var ret = new Roo.factory(cfg);
36808              
36809              ret.render && ret.render(false, ''); // render blank..
36810             this.view = ret;
36811             return ret;
36812         }
36813         return false;
36814     }
36815     \*/
36816 });
36817  
36818 /**
36819  * @class Roo.bootstrap.panel.Grid
36820  * @extends Roo.bootstrap.panel.Content
36821  * @constructor
36822  * Create a new GridPanel.
36823  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36824  * @param {Object} config A the config object
36825   
36826  */
36827
36828
36829
36830 Roo.bootstrap.panel.Grid = function(config)
36831 {
36832     
36833       
36834     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36835         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36836
36837     config.el = this.wrapper;
36838     //this.el = this.wrapper;
36839     
36840       if (config.container) {
36841         // ctor'ed from a Border/panel.grid
36842         
36843         
36844         this.wrapper.setStyle("overflow", "hidden");
36845         this.wrapper.addClass('roo-grid-container');
36846
36847     }
36848     
36849     
36850     if(config.toolbar){
36851         var tool_el = this.wrapper.createChild();    
36852         this.toolbar = Roo.factory(config.toolbar);
36853         var ti = [];
36854         if (config.toolbar.items) {
36855             ti = config.toolbar.items ;
36856             delete config.toolbar.items ;
36857         }
36858         
36859         var nitems = [];
36860         this.toolbar.render(tool_el);
36861         for(var i =0;i < ti.length;i++) {
36862           //  Roo.log(['add child', items[i]]);
36863             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36864         }
36865         this.toolbar.items = nitems;
36866         
36867         delete config.toolbar;
36868     }
36869     
36870     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36871     config.grid.scrollBody = true;;
36872     config.grid.monitorWindowResize = false; // turn off autosizing
36873     config.grid.autoHeight = false;
36874     config.grid.autoWidth = false;
36875     
36876     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36877     
36878     if (config.background) {
36879         // render grid on panel activation (if panel background)
36880         this.on('activate', function(gp) {
36881             if (!gp.grid.rendered) {
36882                 gp.grid.render(this.wrapper);
36883                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36884             }
36885         });
36886             
36887     } else {
36888         this.grid.render(this.wrapper);
36889         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36890
36891     }
36892     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36893     // ??? needed ??? config.el = this.wrapper;
36894     
36895     
36896     
36897   
36898     // xtype created footer. - not sure if will work as we normally have to render first..
36899     if (this.footer && !this.footer.el && this.footer.xtype) {
36900         
36901         var ctr = this.grid.getView().getFooterPanel(true);
36902         this.footer.dataSource = this.grid.dataSource;
36903         this.footer = Roo.factory(this.footer, Roo);
36904         this.footer.render(ctr);
36905         
36906     }
36907     
36908     
36909     
36910     
36911      
36912 };
36913
36914 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36915     getId : function(){
36916         return this.grid.id;
36917     },
36918     
36919     /**
36920      * Returns the grid for this panel
36921      * @return {Roo.bootstrap.Table} 
36922      */
36923     getGrid : function(){
36924         return this.grid;    
36925     },
36926     
36927     setSize : function(width, height){
36928         if(!this.ignoreResize(width, height)){
36929             var grid = this.grid;
36930             var size = this.adjustForComponents(width, height);
36931             var gridel = grid.getGridEl();
36932             gridel.setSize(size.width, size.height);
36933             /*
36934             var thd = grid.getGridEl().select('thead',true).first();
36935             var tbd = grid.getGridEl().select('tbody', true).first();
36936             if (tbd) {
36937                 tbd.setSize(width, height - thd.getHeight());
36938             }
36939             */
36940             grid.autoSize();
36941         }
36942     },
36943      
36944     
36945     
36946     beforeSlide : function(){
36947         this.grid.getView().scroller.clip();
36948     },
36949     
36950     afterSlide : function(){
36951         this.grid.getView().scroller.unclip();
36952     },
36953     
36954     destroy : function(){
36955         this.grid.destroy();
36956         delete this.grid;
36957         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36958     }
36959 });
36960
36961 /**
36962  * @class Roo.bootstrap.panel.Nest
36963  * @extends Roo.bootstrap.panel.Content
36964  * @constructor
36965  * Create a new Panel, that can contain a layout.Border.
36966  * 
36967  * 
36968  * @param {Roo.BorderLayout} layout The layout for this panel
36969  * @param {String/Object} config A string to set only the title or a config object
36970  */
36971 Roo.bootstrap.panel.Nest = function(config)
36972 {
36973     // construct with only one argument..
36974     /* FIXME - implement nicer consturctors
36975     if (layout.layout) {
36976         config = layout;
36977         layout = config.layout;
36978         delete config.layout;
36979     }
36980     if (layout.xtype && !layout.getEl) {
36981         // then layout needs constructing..
36982         layout = Roo.factory(layout, Roo);
36983     }
36984     */
36985     
36986     config.el =  config.layout.getEl();
36987     
36988     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36989     
36990     config.layout.monitorWindowResize = false; // turn off autosizing
36991     this.layout = config.layout;
36992     this.layout.getEl().addClass("roo-layout-nested-layout");
36993     
36994     
36995     
36996     
36997 };
36998
36999 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37000
37001     setSize : function(width, height){
37002         if(!this.ignoreResize(width, height)){
37003             var size = this.adjustForComponents(width, height);
37004             var el = this.layout.getEl();
37005             if (size.height < 1) {
37006                 el.setWidth(size.width);   
37007             } else {
37008                 el.setSize(size.width, size.height);
37009             }
37010             var touch = el.dom.offsetWidth;
37011             this.layout.layout();
37012             // ie requires a double layout on the first pass
37013             if(Roo.isIE && !this.initialized){
37014                 this.initialized = true;
37015                 this.layout.layout();
37016             }
37017         }
37018     },
37019     
37020     // activate all subpanels if not currently active..
37021     
37022     setActiveState : function(active){
37023         this.active = active;
37024         this.setActiveClass(active);
37025         
37026         if(!active){
37027             this.fireEvent("deactivate", this);
37028             return;
37029         }
37030         
37031         this.fireEvent("activate", this);
37032         // not sure if this should happen before or after..
37033         if (!this.layout) {
37034             return; // should not happen..
37035         }
37036         var reg = false;
37037         for (var r in this.layout.regions) {
37038             reg = this.layout.getRegion(r);
37039             if (reg.getActivePanel()) {
37040                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37041                 reg.setActivePanel(reg.getActivePanel());
37042                 continue;
37043             }
37044             if (!reg.panels.length) {
37045                 continue;
37046             }
37047             reg.showPanel(reg.getPanel(0));
37048         }
37049         
37050         
37051         
37052         
37053     },
37054     
37055     /**
37056      * Returns the nested BorderLayout for this panel
37057      * @return {Roo.BorderLayout} 
37058      */
37059     getLayout : function(){
37060         return this.layout;
37061     },
37062     
37063      /**
37064      * Adds a xtype elements to the layout of the nested panel
37065      * <pre><code>
37066
37067 panel.addxtype({
37068        xtype : 'ContentPanel',
37069        region: 'west',
37070        items: [ .... ]
37071    }
37072 );
37073
37074 panel.addxtype({
37075         xtype : 'NestedLayoutPanel',
37076         region: 'west',
37077         layout: {
37078            center: { },
37079            west: { }   
37080         },
37081         items : [ ... list of content panels or nested layout panels.. ]
37082    }
37083 );
37084 </code></pre>
37085      * @param {Object} cfg Xtype definition of item to add.
37086      */
37087     addxtype : function(cfg) {
37088         return this.layout.addxtype(cfg);
37089     
37090     }
37091 });        /*
37092  * Based on:
37093  * Ext JS Library 1.1.1
37094  * Copyright(c) 2006-2007, Ext JS, LLC.
37095  *
37096  * Originally Released Under LGPL - original licence link has changed is not relivant.
37097  *
37098  * Fork - LGPL
37099  * <script type="text/javascript">
37100  */
37101 /**
37102  * @class Roo.TabPanel
37103  * @extends Roo.util.Observable
37104  * A lightweight tab container.
37105  * <br><br>
37106  * Usage:
37107  * <pre><code>
37108 // basic tabs 1, built from existing content
37109 var tabs = new Roo.TabPanel("tabs1");
37110 tabs.addTab("script", "View Script");
37111 tabs.addTab("markup", "View Markup");
37112 tabs.activate("script");
37113
37114 // more advanced tabs, built from javascript
37115 var jtabs = new Roo.TabPanel("jtabs");
37116 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37117
37118 // set up the UpdateManager
37119 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37120 var updater = tab2.getUpdateManager();
37121 updater.setDefaultUrl("ajax1.htm");
37122 tab2.on('activate', updater.refresh, updater, true);
37123
37124 // Use setUrl for Ajax loading
37125 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37126 tab3.setUrl("ajax2.htm", null, true);
37127
37128 // Disabled tab
37129 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37130 tab4.disable();
37131
37132 jtabs.activate("jtabs-1");
37133  * </code></pre>
37134  * @constructor
37135  * Create a new TabPanel.
37136  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37137  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37138  */
37139 Roo.bootstrap.panel.Tabs = function(config){
37140     /**
37141     * The container element for this TabPanel.
37142     * @type Roo.Element
37143     */
37144     this.el = Roo.get(config.el);
37145     delete config.el;
37146     if(config){
37147         if(typeof config == "boolean"){
37148             this.tabPosition = config ? "bottom" : "top";
37149         }else{
37150             Roo.apply(this, config);
37151         }
37152     }
37153     
37154     if(this.tabPosition == "bottom"){
37155         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37156         this.el.addClass("roo-tabs-bottom");
37157     }
37158     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37159     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37160     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37161     if(Roo.isIE){
37162         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37163     }
37164     if(this.tabPosition != "bottom"){
37165         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37166          * @type Roo.Element
37167          */
37168         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37169         this.el.addClass("roo-tabs-top");
37170     }
37171     this.items = [];
37172
37173     this.bodyEl.setStyle("position", "relative");
37174
37175     this.active = null;
37176     this.activateDelegate = this.activate.createDelegate(this);
37177
37178     this.addEvents({
37179         /**
37180          * @event tabchange
37181          * Fires when the active tab changes
37182          * @param {Roo.TabPanel} this
37183          * @param {Roo.TabPanelItem} activePanel The new active tab
37184          */
37185         "tabchange": true,
37186         /**
37187          * @event beforetabchange
37188          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37189          * @param {Roo.TabPanel} this
37190          * @param {Object} e Set cancel to true on this object to cancel the tab change
37191          * @param {Roo.TabPanelItem} tab The tab being changed to
37192          */
37193         "beforetabchange" : true
37194     });
37195
37196     Roo.EventManager.onWindowResize(this.onResize, this);
37197     this.cpad = this.el.getPadding("lr");
37198     this.hiddenCount = 0;
37199
37200
37201     // toolbar on the tabbar support...
37202     if (this.toolbar) {
37203         alert("no toolbar support yet");
37204         this.toolbar  = false;
37205         /*
37206         var tcfg = this.toolbar;
37207         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37208         this.toolbar = new Roo.Toolbar(tcfg);
37209         if (Roo.isSafari) {
37210             var tbl = tcfg.container.child('table', true);
37211             tbl.setAttribute('width', '100%');
37212         }
37213         */
37214         
37215     }
37216    
37217
37218
37219     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37220 };
37221
37222 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37223     /*
37224      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37225      */
37226     tabPosition : "top",
37227     /*
37228      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37229      */
37230     currentTabWidth : 0,
37231     /*
37232      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37233      */
37234     minTabWidth : 40,
37235     /*
37236      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37237      */
37238     maxTabWidth : 250,
37239     /*
37240      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37241      */
37242     preferredTabWidth : 175,
37243     /*
37244      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37245      */
37246     resizeTabs : false,
37247     /*
37248      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37249      */
37250     monitorResize : true,
37251     /*
37252      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37253      */
37254     toolbar : false,
37255
37256     /**
37257      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37258      * @param {String} id The id of the div to use <b>or create</b>
37259      * @param {String} text The text for the tab
37260      * @param {String} content (optional) Content to put in the TabPanelItem body
37261      * @param {Boolean} closable (optional) True to create a close icon on the tab
37262      * @return {Roo.TabPanelItem} The created TabPanelItem
37263      */
37264     addTab : function(id, text, content, closable, tpl)
37265     {
37266         var item = new Roo.bootstrap.panel.TabItem({
37267             panel: this,
37268             id : id,
37269             text : text,
37270             closable : closable,
37271             tpl : tpl
37272         });
37273         this.addTabItem(item);
37274         if(content){
37275             item.setContent(content);
37276         }
37277         return item;
37278     },
37279
37280     /**
37281      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37282      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37283      * @return {Roo.TabPanelItem}
37284      */
37285     getTab : function(id){
37286         return this.items[id];
37287     },
37288
37289     /**
37290      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37291      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37292      */
37293     hideTab : function(id){
37294         var t = this.items[id];
37295         if(!t.isHidden()){
37296            t.setHidden(true);
37297            this.hiddenCount++;
37298            this.autoSizeTabs();
37299         }
37300     },
37301
37302     /**
37303      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37304      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37305      */
37306     unhideTab : function(id){
37307         var t = this.items[id];
37308         if(t.isHidden()){
37309            t.setHidden(false);
37310            this.hiddenCount--;
37311            this.autoSizeTabs();
37312         }
37313     },
37314
37315     /**
37316      * Adds an existing {@link Roo.TabPanelItem}.
37317      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37318      */
37319     addTabItem : function(item){
37320         this.items[item.id] = item;
37321         this.items.push(item);
37322       //  if(this.resizeTabs){
37323     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37324   //         this.autoSizeTabs();
37325 //        }else{
37326 //            item.autoSize();
37327        // }
37328     },
37329
37330     /**
37331      * Removes a {@link Roo.TabPanelItem}.
37332      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37333      */
37334     removeTab : function(id){
37335         var items = this.items;
37336         var tab = items[id];
37337         if(!tab) { return; }
37338         var index = items.indexOf(tab);
37339         if(this.active == tab && items.length > 1){
37340             var newTab = this.getNextAvailable(index);
37341             if(newTab) {
37342                 newTab.activate();
37343             }
37344         }
37345         this.stripEl.dom.removeChild(tab.pnode.dom);
37346         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37347             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37348         }
37349         items.splice(index, 1);
37350         delete this.items[tab.id];
37351         tab.fireEvent("close", tab);
37352         tab.purgeListeners();
37353         this.autoSizeTabs();
37354     },
37355
37356     getNextAvailable : function(start){
37357         var items = this.items;
37358         var index = start;
37359         // look for a next tab that will slide over to
37360         // replace the one being removed
37361         while(index < items.length){
37362             var item = items[++index];
37363             if(item && !item.isHidden()){
37364                 return item;
37365             }
37366         }
37367         // if one isn't found select the previous tab (on the left)
37368         index = start;
37369         while(index >= 0){
37370             var item = items[--index];
37371             if(item && !item.isHidden()){
37372                 return item;
37373             }
37374         }
37375         return null;
37376     },
37377
37378     /**
37379      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37380      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37381      */
37382     disableTab : function(id){
37383         var tab = this.items[id];
37384         if(tab && this.active != tab){
37385             tab.disable();
37386         }
37387     },
37388
37389     /**
37390      * Enables a {@link Roo.TabPanelItem} that is disabled.
37391      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37392      */
37393     enableTab : function(id){
37394         var tab = this.items[id];
37395         tab.enable();
37396     },
37397
37398     /**
37399      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37400      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37401      * @return {Roo.TabPanelItem} The TabPanelItem.
37402      */
37403     activate : function(id){
37404         var tab = this.items[id];
37405         if(!tab){
37406             return null;
37407         }
37408         if(tab == this.active || tab.disabled){
37409             return tab;
37410         }
37411         var e = {};
37412         this.fireEvent("beforetabchange", this, e, tab);
37413         if(e.cancel !== true && !tab.disabled){
37414             if(this.active){
37415                 this.active.hide();
37416             }
37417             this.active = this.items[id];
37418             this.active.show();
37419             this.fireEvent("tabchange", this, this.active);
37420         }
37421         return tab;
37422     },
37423
37424     /**
37425      * Gets the active {@link Roo.TabPanelItem}.
37426      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37427      */
37428     getActiveTab : function(){
37429         return this.active;
37430     },
37431
37432     /**
37433      * Updates the tab body element to fit the height of the container element
37434      * for overflow scrolling
37435      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37436      */
37437     syncHeight : function(targetHeight){
37438         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37439         var bm = this.bodyEl.getMargins();
37440         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37441         this.bodyEl.setHeight(newHeight);
37442         return newHeight;
37443     },
37444
37445     onResize : function(){
37446         if(this.monitorResize){
37447             this.autoSizeTabs();
37448         }
37449     },
37450
37451     /**
37452      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37453      */
37454     beginUpdate : function(){
37455         this.updating = true;
37456     },
37457
37458     /**
37459      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37460      */
37461     endUpdate : function(){
37462         this.updating = false;
37463         this.autoSizeTabs();
37464     },
37465
37466     /**
37467      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37468      */
37469     autoSizeTabs : function(){
37470         var count = this.items.length;
37471         var vcount = count - this.hiddenCount;
37472         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37473             return;
37474         }
37475         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37476         var availWidth = Math.floor(w / vcount);
37477         var b = this.stripBody;
37478         if(b.getWidth() > w){
37479             var tabs = this.items;
37480             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37481             if(availWidth < this.minTabWidth){
37482                 /*if(!this.sleft){    // incomplete scrolling code
37483                     this.createScrollButtons();
37484                 }
37485                 this.showScroll();
37486                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37487             }
37488         }else{
37489             if(this.currentTabWidth < this.preferredTabWidth){
37490                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37491             }
37492         }
37493     },
37494
37495     /**
37496      * Returns the number of tabs in this TabPanel.
37497      * @return {Number}
37498      */
37499      getCount : function(){
37500          return this.items.length;
37501      },
37502
37503     /**
37504      * Resizes all the tabs to the passed width
37505      * @param {Number} The new width
37506      */
37507     setTabWidth : function(width){
37508         this.currentTabWidth = width;
37509         for(var i = 0, len = this.items.length; i < len; i++) {
37510                 if(!this.items[i].isHidden()) {
37511                 this.items[i].setWidth(width);
37512             }
37513         }
37514     },
37515
37516     /**
37517      * Destroys this TabPanel
37518      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37519      */
37520     destroy : function(removeEl){
37521         Roo.EventManager.removeResizeListener(this.onResize, this);
37522         for(var i = 0, len = this.items.length; i < len; i++){
37523             this.items[i].purgeListeners();
37524         }
37525         if(removeEl === true){
37526             this.el.update("");
37527             this.el.remove();
37528         }
37529     },
37530     
37531     createStrip : function(container)
37532     {
37533         var strip = document.createElement("nav");
37534         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37535         container.appendChild(strip);
37536         return strip;
37537     },
37538     
37539     createStripList : function(strip)
37540     {
37541         // div wrapper for retard IE
37542         // returns the "tr" element.
37543         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37544         //'<div class="x-tabs-strip-wrap">'+
37545           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37546           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37547         return strip.firstChild; //.firstChild.firstChild.firstChild;
37548     },
37549     createBody : function(container)
37550     {
37551         var body = document.createElement("div");
37552         Roo.id(body, "tab-body");
37553         //Roo.fly(body).addClass("x-tabs-body");
37554         Roo.fly(body).addClass("tab-content");
37555         container.appendChild(body);
37556         return body;
37557     },
37558     createItemBody :function(bodyEl, id){
37559         var body = Roo.getDom(id);
37560         if(!body){
37561             body = document.createElement("div");
37562             body.id = id;
37563         }
37564         //Roo.fly(body).addClass("x-tabs-item-body");
37565         Roo.fly(body).addClass("tab-pane");
37566          bodyEl.insertBefore(body, bodyEl.firstChild);
37567         return body;
37568     },
37569     /** @private */
37570     createStripElements :  function(stripEl, text, closable, tpl)
37571     {
37572         var td = document.createElement("li"); // was td..
37573         
37574         
37575         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37576         
37577         
37578         stripEl.appendChild(td);
37579         /*if(closable){
37580             td.className = "x-tabs-closable";
37581             if(!this.closeTpl){
37582                 this.closeTpl = new Roo.Template(
37583                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37584                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37585                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37586                 );
37587             }
37588             var el = this.closeTpl.overwrite(td, {"text": text});
37589             var close = el.getElementsByTagName("div")[0];
37590             var inner = el.getElementsByTagName("em")[0];
37591             return {"el": el, "close": close, "inner": inner};
37592         } else {
37593         */
37594         // not sure what this is..
37595 //            if(!this.tabTpl){
37596                 //this.tabTpl = new Roo.Template(
37597                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37598                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37599                 //);
37600 //                this.tabTpl = new Roo.Template(
37601 //                   '<a href="#">' +
37602 //                   '<span unselectable="on"' +
37603 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37604 //                            ' >{text}</span></a>'
37605 //                );
37606 //                
37607 //            }
37608
37609
37610             var template = tpl || this.tabTpl || false;
37611             
37612             if(!template){
37613                 
37614                 template = new Roo.Template(
37615                    '<a href="#">' +
37616                    '<span unselectable="on"' +
37617                             (this.disableTooltips ? '' : ' title="{text}"') +
37618                             ' >{text}</span></a>'
37619                 );
37620             }
37621             
37622             switch (typeof(template)) {
37623                 case 'object' :
37624                     break;
37625                 case 'string' :
37626                     template = new Roo.Template(template);
37627                     break;
37628                 default :
37629                     break;
37630             }
37631             
37632             var el = template.overwrite(td, {"text": text});
37633             
37634             var inner = el.getElementsByTagName("span")[0];
37635             
37636             return {"el": el, "inner": inner};
37637             
37638     }
37639         
37640     
37641 });
37642
37643 /**
37644  * @class Roo.TabPanelItem
37645  * @extends Roo.util.Observable
37646  * Represents an individual item (tab plus body) in a TabPanel.
37647  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37648  * @param {String} id The id of this TabPanelItem
37649  * @param {String} text The text for the tab of this TabPanelItem
37650  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37651  */
37652 Roo.bootstrap.panel.TabItem = function(config){
37653     /**
37654      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37655      * @type Roo.TabPanel
37656      */
37657     this.tabPanel = config.panel;
37658     /**
37659      * The id for this TabPanelItem
37660      * @type String
37661      */
37662     this.id = config.id;
37663     /** @private */
37664     this.disabled = false;
37665     /** @private */
37666     this.text = config.text;
37667     /** @private */
37668     this.loaded = false;
37669     this.closable = config.closable;
37670
37671     /**
37672      * The body element for this TabPanelItem.
37673      * @type Roo.Element
37674      */
37675     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37676     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37677     this.bodyEl.setStyle("display", "block");
37678     this.bodyEl.setStyle("zoom", "1");
37679     //this.hideAction();
37680
37681     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37682     /** @private */
37683     this.el = Roo.get(els.el);
37684     this.inner = Roo.get(els.inner, true);
37685     this.textEl = Roo.get(this.el.dom.firstChild, true);
37686     this.pnode = Roo.get(els.el.parentNode, true);
37687 //    this.el.on("mousedown", this.onTabMouseDown, this);
37688     this.el.on("click", this.onTabClick, this);
37689     /** @private */
37690     if(config.closable){
37691         var c = Roo.get(els.close, true);
37692         c.dom.title = this.closeText;
37693         c.addClassOnOver("close-over");
37694         c.on("click", this.closeClick, this);
37695      }
37696
37697     this.addEvents({
37698          /**
37699          * @event activate
37700          * Fires when this tab becomes the active tab.
37701          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37702          * @param {Roo.TabPanelItem} this
37703          */
37704         "activate": true,
37705         /**
37706          * @event beforeclose
37707          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37708          * @param {Roo.TabPanelItem} this
37709          * @param {Object} e Set cancel to true on this object to cancel the close.
37710          */
37711         "beforeclose": true,
37712         /**
37713          * @event close
37714          * Fires when this tab is closed.
37715          * @param {Roo.TabPanelItem} this
37716          */
37717          "close": true,
37718         /**
37719          * @event deactivate
37720          * Fires when this tab is no longer the active tab.
37721          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37722          * @param {Roo.TabPanelItem} this
37723          */
37724          "deactivate" : true
37725     });
37726     this.hidden = false;
37727
37728     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37729 };
37730
37731 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37732            {
37733     purgeListeners : function(){
37734        Roo.util.Observable.prototype.purgeListeners.call(this);
37735        this.el.removeAllListeners();
37736     },
37737     /**
37738      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37739      */
37740     show : function(){
37741         this.pnode.addClass("active");
37742         this.showAction();
37743         if(Roo.isOpera){
37744             this.tabPanel.stripWrap.repaint();
37745         }
37746         this.fireEvent("activate", this.tabPanel, this);
37747     },
37748
37749     /**
37750      * Returns true if this tab is the active tab.
37751      * @return {Boolean}
37752      */
37753     isActive : function(){
37754         return this.tabPanel.getActiveTab() == this;
37755     },
37756
37757     /**
37758      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37759      */
37760     hide : function(){
37761         this.pnode.removeClass("active");
37762         this.hideAction();
37763         this.fireEvent("deactivate", this.tabPanel, this);
37764     },
37765
37766     hideAction : function(){
37767         this.bodyEl.hide();
37768         this.bodyEl.setStyle("position", "absolute");
37769         this.bodyEl.setLeft("-20000px");
37770         this.bodyEl.setTop("-20000px");
37771     },
37772
37773     showAction : function(){
37774         this.bodyEl.setStyle("position", "relative");
37775         this.bodyEl.setTop("");
37776         this.bodyEl.setLeft("");
37777         this.bodyEl.show();
37778     },
37779
37780     /**
37781      * Set the tooltip for the tab.
37782      * @param {String} tooltip The tab's tooltip
37783      */
37784     setTooltip : function(text){
37785         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37786             this.textEl.dom.qtip = text;
37787             this.textEl.dom.removeAttribute('title');
37788         }else{
37789             this.textEl.dom.title = text;
37790         }
37791     },
37792
37793     onTabClick : function(e){
37794         e.preventDefault();
37795         this.tabPanel.activate(this.id);
37796     },
37797
37798     onTabMouseDown : function(e){
37799         e.preventDefault();
37800         this.tabPanel.activate(this.id);
37801     },
37802 /*
37803     getWidth : function(){
37804         return this.inner.getWidth();
37805     },
37806
37807     setWidth : function(width){
37808         var iwidth = width - this.pnode.getPadding("lr");
37809         this.inner.setWidth(iwidth);
37810         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37811         this.pnode.setWidth(width);
37812     },
37813 */
37814     /**
37815      * Show or hide the tab
37816      * @param {Boolean} hidden True to hide or false to show.
37817      */
37818     setHidden : function(hidden){
37819         this.hidden = hidden;
37820         this.pnode.setStyle("display", hidden ? "none" : "");
37821     },
37822
37823     /**
37824      * Returns true if this tab is "hidden"
37825      * @return {Boolean}
37826      */
37827     isHidden : function(){
37828         return this.hidden;
37829     },
37830
37831     /**
37832      * Returns the text for this tab
37833      * @return {String}
37834      */
37835     getText : function(){
37836         return this.text;
37837     },
37838     /*
37839     autoSize : function(){
37840         //this.el.beginMeasure();
37841         this.textEl.setWidth(1);
37842         /*
37843          *  #2804 [new] Tabs in Roojs
37844          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37845          */
37846         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37847         //this.el.endMeasure();
37848     //},
37849
37850     /**
37851      * Sets the text for the tab (Note: this also sets the tooltip text)
37852      * @param {String} text The tab's text and tooltip
37853      */
37854     setText : function(text){
37855         this.text = text;
37856         this.textEl.update(text);
37857         this.setTooltip(text);
37858         //if(!this.tabPanel.resizeTabs){
37859         //    this.autoSize();
37860         //}
37861     },
37862     /**
37863      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37864      */
37865     activate : function(){
37866         this.tabPanel.activate(this.id);
37867     },
37868
37869     /**
37870      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37871      */
37872     disable : function(){
37873         if(this.tabPanel.active != this){
37874             this.disabled = true;
37875             this.pnode.addClass("disabled");
37876         }
37877     },
37878
37879     /**
37880      * Enables this TabPanelItem if it was previously disabled.
37881      */
37882     enable : function(){
37883         this.disabled = false;
37884         this.pnode.removeClass("disabled");
37885     },
37886
37887     /**
37888      * Sets the content for this TabPanelItem.
37889      * @param {String} content The content
37890      * @param {Boolean} loadScripts true to look for and load scripts
37891      */
37892     setContent : function(content, loadScripts){
37893         this.bodyEl.update(content, loadScripts);
37894     },
37895
37896     /**
37897      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37898      * @return {Roo.UpdateManager} The UpdateManager
37899      */
37900     getUpdateManager : function(){
37901         return this.bodyEl.getUpdateManager();
37902     },
37903
37904     /**
37905      * Set a URL to be used to load the content for this TabPanelItem.
37906      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37907      * @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)
37908      * @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)
37909      * @return {Roo.UpdateManager} The UpdateManager
37910      */
37911     setUrl : function(url, params, loadOnce){
37912         if(this.refreshDelegate){
37913             this.un('activate', this.refreshDelegate);
37914         }
37915         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37916         this.on("activate", this.refreshDelegate);
37917         return this.bodyEl.getUpdateManager();
37918     },
37919
37920     /** @private */
37921     _handleRefresh : function(url, params, loadOnce){
37922         if(!loadOnce || !this.loaded){
37923             var updater = this.bodyEl.getUpdateManager();
37924             updater.update(url, params, this._setLoaded.createDelegate(this));
37925         }
37926     },
37927
37928     /**
37929      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37930      *   Will fail silently if the setUrl method has not been called.
37931      *   This does not activate the panel, just updates its content.
37932      */
37933     refresh : function(){
37934         if(this.refreshDelegate){
37935            this.loaded = false;
37936            this.refreshDelegate();
37937         }
37938     },
37939
37940     /** @private */
37941     _setLoaded : function(){
37942         this.loaded = true;
37943     },
37944
37945     /** @private */
37946     closeClick : function(e){
37947         var o = {};
37948         e.stopEvent();
37949         this.fireEvent("beforeclose", this, o);
37950         if(o.cancel !== true){
37951             this.tabPanel.removeTab(this.id);
37952         }
37953     },
37954     /**
37955      * The text displayed in the tooltip for the close icon.
37956      * @type String
37957      */
37958     closeText : "Close this tab"
37959 });
37960 /**
37961 *    This script refer to:
37962 *    Title: International Telephone Input
37963 *    Author: Jack O'Connor
37964 *    Code version:  v12.1.12
37965 *    Availability: https://github.com/jackocnr/intl-tel-input.git
37966 **/
37967
37968 Roo.bootstrap.PhoneInputData = function() {
37969     var d = [
37970       [
37971         "Afghanistan (‫افغانستان‬‎)",
37972         "af",
37973         "93"
37974       ],
37975       [
37976         "Albania (Shqipëri)",
37977         "al",
37978         "355"
37979       ],
37980       [
37981         "Algeria (‫الجزائر‬‎)",
37982         "dz",
37983         "213"
37984       ],
37985       [
37986         "American Samoa",
37987         "as",
37988         "1684"
37989       ],
37990       [
37991         "Andorra",
37992         "ad",
37993         "376"
37994       ],
37995       [
37996         "Angola",
37997         "ao",
37998         "244"
37999       ],
38000       [
38001         "Anguilla",
38002         "ai",
38003         "1264"
38004       ],
38005       [
38006         "Antigua and Barbuda",
38007         "ag",
38008         "1268"
38009       ],
38010       [
38011         "Argentina",
38012         "ar",
38013         "54"
38014       ],
38015       [
38016         "Armenia (Հայաստան)",
38017         "am",
38018         "374"
38019       ],
38020       [
38021         "Aruba",
38022         "aw",
38023         "297"
38024       ],
38025       [
38026         "Australia",
38027         "au",
38028         "61",
38029         0
38030       ],
38031       [
38032         "Austria (Österreich)",
38033         "at",
38034         "43"
38035       ],
38036       [
38037         "Azerbaijan (Azərbaycan)",
38038         "az",
38039         "994"
38040       ],
38041       [
38042         "Bahamas",
38043         "bs",
38044         "1242"
38045       ],
38046       [
38047         "Bahrain (‫البحرين‬‎)",
38048         "bh",
38049         "973"
38050       ],
38051       [
38052         "Bangladesh (বাংলাদেশ)",
38053         "bd",
38054         "880"
38055       ],
38056       [
38057         "Barbados",
38058         "bb",
38059         "1246"
38060       ],
38061       [
38062         "Belarus (Беларусь)",
38063         "by",
38064         "375"
38065       ],
38066       [
38067         "Belgium (België)",
38068         "be",
38069         "32"
38070       ],
38071       [
38072         "Belize",
38073         "bz",
38074         "501"
38075       ],
38076       [
38077         "Benin (Bénin)",
38078         "bj",
38079         "229"
38080       ],
38081       [
38082         "Bermuda",
38083         "bm",
38084         "1441"
38085       ],
38086       [
38087         "Bhutan (འབྲུག)",
38088         "bt",
38089         "975"
38090       ],
38091       [
38092         "Bolivia",
38093         "bo",
38094         "591"
38095       ],
38096       [
38097         "Bosnia and Herzegovina (Босна и Херцеговина)",
38098         "ba",
38099         "387"
38100       ],
38101       [
38102         "Botswana",
38103         "bw",
38104         "267"
38105       ],
38106       [
38107         "Brazil (Brasil)",
38108         "br",
38109         "55"
38110       ],
38111       [
38112         "British Indian Ocean Territory",
38113         "io",
38114         "246"
38115       ],
38116       [
38117         "British Virgin Islands",
38118         "vg",
38119         "1284"
38120       ],
38121       [
38122         "Brunei",
38123         "bn",
38124         "673"
38125       ],
38126       [
38127         "Bulgaria (България)",
38128         "bg",
38129         "359"
38130       ],
38131       [
38132         "Burkina Faso",
38133         "bf",
38134         "226"
38135       ],
38136       [
38137         "Burundi (Uburundi)",
38138         "bi",
38139         "257"
38140       ],
38141       [
38142         "Cambodia (កម្ពុជា)",
38143         "kh",
38144         "855"
38145       ],
38146       [
38147         "Cameroon (Cameroun)",
38148         "cm",
38149         "237"
38150       ],
38151       [
38152         "Canada",
38153         "ca",
38154         "1",
38155         1,
38156         ["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"]
38157       ],
38158       [
38159         "Cape Verde (Kabu Verdi)",
38160         "cv",
38161         "238"
38162       ],
38163       [
38164         "Caribbean Netherlands",
38165         "bq",
38166         "599",
38167         1
38168       ],
38169       [
38170         "Cayman Islands",
38171         "ky",
38172         "1345"
38173       ],
38174       [
38175         "Central African Republic (République centrafricaine)",
38176         "cf",
38177         "236"
38178       ],
38179       [
38180         "Chad (Tchad)",
38181         "td",
38182         "235"
38183       ],
38184       [
38185         "Chile",
38186         "cl",
38187         "56"
38188       ],
38189       [
38190         "China (中国)",
38191         "cn",
38192         "86"
38193       ],
38194       [
38195         "Christmas Island",
38196         "cx",
38197         "61",
38198         2
38199       ],
38200       [
38201         "Cocos (Keeling) Islands",
38202         "cc",
38203         "61",
38204         1
38205       ],
38206       [
38207         "Colombia",
38208         "co",
38209         "57"
38210       ],
38211       [
38212         "Comoros (‫جزر القمر‬‎)",
38213         "km",
38214         "269"
38215       ],
38216       [
38217         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38218         "cd",
38219         "243"
38220       ],
38221       [
38222         "Congo (Republic) (Congo-Brazzaville)",
38223         "cg",
38224         "242"
38225       ],
38226       [
38227         "Cook Islands",
38228         "ck",
38229         "682"
38230       ],
38231       [
38232         "Costa Rica",
38233         "cr",
38234         "506"
38235       ],
38236       [
38237         "Côte d’Ivoire",
38238         "ci",
38239         "225"
38240       ],
38241       [
38242         "Croatia (Hrvatska)",
38243         "hr",
38244         "385"
38245       ],
38246       [
38247         "Cuba",
38248         "cu",
38249         "53"
38250       ],
38251       [
38252         "Curaçao",
38253         "cw",
38254         "599",
38255         0
38256       ],
38257       [
38258         "Cyprus (Κύπρος)",
38259         "cy",
38260         "357"
38261       ],
38262       [
38263         "Czech Republic (Česká republika)",
38264         "cz",
38265         "420"
38266       ],
38267       [
38268         "Denmark (Danmark)",
38269         "dk",
38270         "45"
38271       ],
38272       [
38273         "Djibouti",
38274         "dj",
38275         "253"
38276       ],
38277       [
38278         "Dominica",
38279         "dm",
38280         "1767"
38281       ],
38282       [
38283         "Dominican Republic (República Dominicana)",
38284         "do",
38285         "1",
38286         2,
38287         ["809", "829", "849"]
38288       ],
38289       [
38290         "Ecuador",
38291         "ec",
38292         "593"
38293       ],
38294       [
38295         "Egypt (‫مصر‬‎)",
38296         "eg",
38297         "20"
38298       ],
38299       [
38300         "El Salvador",
38301         "sv",
38302         "503"
38303       ],
38304       [
38305         "Equatorial Guinea (Guinea Ecuatorial)",
38306         "gq",
38307         "240"
38308       ],
38309       [
38310         "Eritrea",
38311         "er",
38312         "291"
38313       ],
38314       [
38315         "Estonia (Eesti)",
38316         "ee",
38317         "372"
38318       ],
38319       [
38320         "Ethiopia",
38321         "et",
38322         "251"
38323       ],
38324       [
38325         "Falkland Islands (Islas Malvinas)",
38326         "fk",
38327         "500"
38328       ],
38329       [
38330         "Faroe Islands (Føroyar)",
38331         "fo",
38332         "298"
38333       ],
38334       [
38335         "Fiji",
38336         "fj",
38337         "679"
38338       ],
38339       [
38340         "Finland (Suomi)",
38341         "fi",
38342         "358",
38343         0
38344       ],
38345       [
38346         "France",
38347         "fr",
38348         "33"
38349       ],
38350       [
38351         "French Guiana (Guyane française)",
38352         "gf",
38353         "594"
38354       ],
38355       [
38356         "French Polynesia (Polynésie française)",
38357         "pf",
38358         "689"
38359       ],
38360       [
38361         "Gabon",
38362         "ga",
38363         "241"
38364       ],
38365       [
38366         "Gambia",
38367         "gm",
38368         "220"
38369       ],
38370       [
38371         "Georgia (საქართველო)",
38372         "ge",
38373         "995"
38374       ],
38375       [
38376         "Germany (Deutschland)",
38377         "de",
38378         "49"
38379       ],
38380       [
38381         "Ghana (Gaana)",
38382         "gh",
38383         "233"
38384       ],
38385       [
38386         "Gibraltar",
38387         "gi",
38388         "350"
38389       ],
38390       [
38391         "Greece (Ελλάδα)",
38392         "gr",
38393         "30"
38394       ],
38395       [
38396         "Greenland (Kalaallit Nunaat)",
38397         "gl",
38398         "299"
38399       ],
38400       [
38401         "Grenada",
38402         "gd",
38403         "1473"
38404       ],
38405       [
38406         "Guadeloupe",
38407         "gp",
38408         "590",
38409         0
38410       ],
38411       [
38412         "Guam",
38413         "gu",
38414         "1671"
38415       ],
38416       [
38417         "Guatemala",
38418         "gt",
38419         "502"
38420       ],
38421       [
38422         "Guernsey",
38423         "gg",
38424         "44",
38425         1
38426       ],
38427       [
38428         "Guinea (Guinée)",
38429         "gn",
38430         "224"
38431       ],
38432       [
38433         "Guinea-Bissau (Guiné Bissau)",
38434         "gw",
38435         "245"
38436       ],
38437       [
38438         "Guyana",
38439         "gy",
38440         "592"
38441       ],
38442       [
38443         "Haiti",
38444         "ht",
38445         "509"
38446       ],
38447       [
38448         "Honduras",
38449         "hn",
38450         "504"
38451       ],
38452       [
38453         "Hong Kong (香港)",
38454         "hk",
38455         "852"
38456       ],
38457       [
38458         "Hungary (Magyarország)",
38459         "hu",
38460         "36"
38461       ],
38462       [
38463         "Iceland (Ísland)",
38464         "is",
38465         "354"
38466       ],
38467       [
38468         "India (भारत)",
38469         "in",
38470         "91"
38471       ],
38472       [
38473         "Indonesia",
38474         "id",
38475         "62"
38476       ],
38477       [
38478         "Iran (‫ایران‬‎)",
38479         "ir",
38480         "98"
38481       ],
38482       [
38483         "Iraq (‫العراق‬‎)",
38484         "iq",
38485         "964"
38486       ],
38487       [
38488         "Ireland",
38489         "ie",
38490         "353"
38491       ],
38492       [
38493         "Isle of Man",
38494         "im",
38495         "44",
38496         2
38497       ],
38498       [
38499         "Israel (‫ישראל‬‎)",
38500         "il",
38501         "972"
38502       ],
38503       [
38504         "Italy (Italia)",
38505         "it",
38506         "39",
38507         0
38508       ],
38509       [
38510         "Jamaica",
38511         "jm",
38512         "1876"
38513       ],
38514       [
38515         "Japan (日本)",
38516         "jp",
38517         "81"
38518       ],
38519       [
38520         "Jersey",
38521         "je",
38522         "44",
38523         3
38524       ],
38525       [
38526         "Jordan (‫الأردن‬‎)",
38527         "jo",
38528         "962"
38529       ],
38530       [
38531         "Kazakhstan (Казахстан)",
38532         "kz",
38533         "7",
38534         1
38535       ],
38536       [
38537         "Kenya",
38538         "ke",
38539         "254"
38540       ],
38541       [
38542         "Kiribati",
38543         "ki",
38544         "686"
38545       ],
38546       [
38547         "Kosovo",
38548         "xk",
38549         "383"
38550       ],
38551       [
38552         "Kuwait (‫الكويت‬‎)",
38553         "kw",
38554         "965"
38555       ],
38556       [
38557         "Kyrgyzstan (Кыргызстан)",
38558         "kg",
38559         "996"
38560       ],
38561       [
38562         "Laos (ລາວ)",
38563         "la",
38564         "856"
38565       ],
38566       [
38567         "Latvia (Latvija)",
38568         "lv",
38569         "371"
38570       ],
38571       [
38572         "Lebanon (‫لبنان‬‎)",
38573         "lb",
38574         "961"
38575       ],
38576       [
38577         "Lesotho",
38578         "ls",
38579         "266"
38580       ],
38581       [
38582         "Liberia",
38583         "lr",
38584         "231"
38585       ],
38586       [
38587         "Libya (‫ليبيا‬‎)",
38588         "ly",
38589         "218"
38590       ],
38591       [
38592         "Liechtenstein",
38593         "li",
38594         "423"
38595       ],
38596       [
38597         "Lithuania (Lietuva)",
38598         "lt",
38599         "370"
38600       ],
38601       [
38602         "Luxembourg",
38603         "lu",
38604         "352"
38605       ],
38606       [
38607         "Macau (澳門)",
38608         "mo",
38609         "853"
38610       ],
38611       [
38612         "Macedonia (FYROM) (Македонија)",
38613         "mk",
38614         "389"
38615       ],
38616       [
38617         "Madagascar (Madagasikara)",
38618         "mg",
38619         "261"
38620       ],
38621       [
38622         "Malawi",
38623         "mw",
38624         "265"
38625       ],
38626       [
38627         "Malaysia",
38628         "my",
38629         "60"
38630       ],
38631       [
38632         "Maldives",
38633         "mv",
38634         "960"
38635       ],
38636       [
38637         "Mali",
38638         "ml",
38639         "223"
38640       ],
38641       [
38642         "Malta",
38643         "mt",
38644         "356"
38645       ],
38646       [
38647         "Marshall Islands",
38648         "mh",
38649         "692"
38650       ],
38651       [
38652         "Martinique",
38653         "mq",
38654         "596"
38655       ],
38656       [
38657         "Mauritania (‫موريتانيا‬‎)",
38658         "mr",
38659         "222"
38660       ],
38661       [
38662         "Mauritius (Moris)",
38663         "mu",
38664         "230"
38665       ],
38666       [
38667         "Mayotte",
38668         "yt",
38669         "262",
38670         1
38671       ],
38672       [
38673         "Mexico (México)",
38674         "mx",
38675         "52"
38676       ],
38677       [
38678         "Micronesia",
38679         "fm",
38680         "691"
38681       ],
38682       [
38683         "Moldova (Republica Moldova)",
38684         "md",
38685         "373"
38686       ],
38687       [
38688         "Monaco",
38689         "mc",
38690         "377"
38691       ],
38692       [
38693         "Mongolia (Монгол)",
38694         "mn",
38695         "976"
38696       ],
38697       [
38698         "Montenegro (Crna Gora)",
38699         "me",
38700         "382"
38701       ],
38702       [
38703         "Montserrat",
38704         "ms",
38705         "1664"
38706       ],
38707       [
38708         "Morocco (‫المغرب‬‎)",
38709         "ma",
38710         "212",
38711         0
38712       ],
38713       [
38714         "Mozambique (Moçambique)",
38715         "mz",
38716         "258"
38717       ],
38718       [
38719         "Myanmar (Burma) (မြန်မာ)",
38720         "mm",
38721         "95"
38722       ],
38723       [
38724         "Namibia (Namibië)",
38725         "na",
38726         "264"
38727       ],
38728       [
38729         "Nauru",
38730         "nr",
38731         "674"
38732       ],
38733       [
38734         "Nepal (नेपाल)",
38735         "np",
38736         "977"
38737       ],
38738       [
38739         "Netherlands (Nederland)",
38740         "nl",
38741         "31"
38742       ],
38743       [
38744         "New Caledonia (Nouvelle-Calédonie)",
38745         "nc",
38746         "687"
38747       ],
38748       [
38749         "New Zealand",
38750         "nz",
38751         "64"
38752       ],
38753       [
38754         "Nicaragua",
38755         "ni",
38756         "505"
38757       ],
38758       [
38759         "Niger (Nijar)",
38760         "ne",
38761         "227"
38762       ],
38763       [
38764         "Nigeria",
38765         "ng",
38766         "234"
38767       ],
38768       [
38769         "Niue",
38770         "nu",
38771         "683"
38772       ],
38773       [
38774         "Norfolk Island",
38775         "nf",
38776         "672"
38777       ],
38778       [
38779         "North Korea (조선 민주주의 인민 공화국)",
38780         "kp",
38781         "850"
38782       ],
38783       [
38784         "Northern Mariana Islands",
38785         "mp",
38786         "1670"
38787       ],
38788       [
38789         "Norway (Norge)",
38790         "no",
38791         "47",
38792         0
38793       ],
38794       [
38795         "Oman (‫عُمان‬‎)",
38796         "om",
38797         "968"
38798       ],
38799       [
38800         "Pakistan (‫پاکستان‬‎)",
38801         "pk",
38802         "92"
38803       ],
38804       [
38805         "Palau",
38806         "pw",
38807         "680"
38808       ],
38809       [
38810         "Palestine (‫فلسطين‬‎)",
38811         "ps",
38812         "970"
38813       ],
38814       [
38815         "Panama (Panamá)",
38816         "pa",
38817         "507"
38818       ],
38819       [
38820         "Papua New Guinea",
38821         "pg",
38822         "675"
38823       ],
38824       [
38825         "Paraguay",
38826         "py",
38827         "595"
38828       ],
38829       [
38830         "Peru (Perú)",
38831         "pe",
38832         "51"
38833       ],
38834       [
38835         "Philippines",
38836         "ph",
38837         "63"
38838       ],
38839       [
38840         "Poland (Polska)",
38841         "pl",
38842         "48"
38843       ],
38844       [
38845         "Portugal",
38846         "pt",
38847         "351"
38848       ],
38849       [
38850         "Puerto Rico",
38851         "pr",
38852         "1",
38853         3,
38854         ["787", "939"]
38855       ],
38856       [
38857         "Qatar (‫قطر‬‎)",
38858         "qa",
38859         "974"
38860       ],
38861       [
38862         "Réunion (La Réunion)",
38863         "re",
38864         "262",
38865         0
38866       ],
38867       [
38868         "Romania (România)",
38869         "ro",
38870         "40"
38871       ],
38872       [
38873         "Russia (Россия)",
38874         "ru",
38875         "7",
38876         0
38877       ],
38878       [
38879         "Rwanda",
38880         "rw",
38881         "250"
38882       ],
38883       [
38884         "Saint Barthélemy",
38885         "bl",
38886         "590",
38887         1
38888       ],
38889       [
38890         "Saint Helena",
38891         "sh",
38892         "290"
38893       ],
38894       [
38895         "Saint Kitts and Nevis",
38896         "kn",
38897         "1869"
38898       ],
38899       [
38900         "Saint Lucia",
38901         "lc",
38902         "1758"
38903       ],
38904       [
38905         "Saint Martin (Saint-Martin (partie française))",
38906         "mf",
38907         "590",
38908         2
38909       ],
38910       [
38911         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38912         "pm",
38913         "508"
38914       ],
38915       [
38916         "Saint Vincent and the Grenadines",
38917         "vc",
38918         "1784"
38919       ],
38920       [
38921         "Samoa",
38922         "ws",
38923         "685"
38924       ],
38925       [
38926         "San Marino",
38927         "sm",
38928         "378"
38929       ],
38930       [
38931         "São Tomé and Príncipe (São Tomé e Príncipe)",
38932         "st",
38933         "239"
38934       ],
38935       [
38936         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
38937         "sa",
38938         "966"
38939       ],
38940       [
38941         "Senegal (Sénégal)",
38942         "sn",
38943         "221"
38944       ],
38945       [
38946         "Serbia (Србија)",
38947         "rs",
38948         "381"
38949       ],
38950       [
38951         "Seychelles",
38952         "sc",
38953         "248"
38954       ],
38955       [
38956         "Sierra Leone",
38957         "sl",
38958         "232"
38959       ],
38960       [
38961         "Singapore",
38962         "sg",
38963         "65"
38964       ],
38965       [
38966         "Sint Maarten",
38967         "sx",
38968         "1721"
38969       ],
38970       [
38971         "Slovakia (Slovensko)",
38972         "sk",
38973         "421"
38974       ],
38975       [
38976         "Slovenia (Slovenija)",
38977         "si",
38978         "386"
38979       ],
38980       [
38981         "Solomon Islands",
38982         "sb",
38983         "677"
38984       ],
38985       [
38986         "Somalia (Soomaaliya)",
38987         "so",
38988         "252"
38989       ],
38990       [
38991         "South Africa",
38992         "za",
38993         "27"
38994       ],
38995       [
38996         "South Korea (대한민국)",
38997         "kr",
38998         "82"
38999       ],
39000       [
39001         "South Sudan (‫جنوب السودان‬‎)",
39002         "ss",
39003         "211"
39004       ],
39005       [
39006         "Spain (España)",
39007         "es",
39008         "34"
39009       ],
39010       [
39011         "Sri Lanka (ශ්‍රී ලංකාව)",
39012         "lk",
39013         "94"
39014       ],
39015       [
39016         "Sudan (‫السودان‬‎)",
39017         "sd",
39018         "249"
39019       ],
39020       [
39021         "Suriname",
39022         "sr",
39023         "597"
39024       ],
39025       [
39026         "Svalbard and Jan Mayen",
39027         "sj",
39028         "47",
39029         1
39030       ],
39031       [
39032         "Swaziland",
39033         "sz",
39034         "268"
39035       ],
39036       [
39037         "Sweden (Sverige)",
39038         "se",
39039         "46"
39040       ],
39041       [
39042         "Switzerland (Schweiz)",
39043         "ch",
39044         "41"
39045       ],
39046       [
39047         "Syria (‫سوريا‬‎)",
39048         "sy",
39049         "963"
39050       ],
39051       [
39052         "Taiwan (台灣)",
39053         "tw",
39054         "886"
39055       ],
39056       [
39057         "Tajikistan",
39058         "tj",
39059         "992"
39060       ],
39061       [
39062         "Tanzania",
39063         "tz",
39064         "255"
39065       ],
39066       [
39067         "Thailand (ไทย)",
39068         "th",
39069         "66"
39070       ],
39071       [
39072         "Timor-Leste",
39073         "tl",
39074         "670"
39075       ],
39076       [
39077         "Togo",
39078         "tg",
39079         "228"
39080       ],
39081       [
39082         "Tokelau",
39083         "tk",
39084         "690"
39085       ],
39086       [
39087         "Tonga",
39088         "to",
39089         "676"
39090       ],
39091       [
39092         "Trinidad and Tobago",
39093         "tt",
39094         "1868"
39095       ],
39096       [
39097         "Tunisia (‫تونس‬‎)",
39098         "tn",
39099         "216"
39100       ],
39101       [
39102         "Turkey (Türkiye)",
39103         "tr",
39104         "90"
39105       ],
39106       [
39107         "Turkmenistan",
39108         "tm",
39109         "993"
39110       ],
39111       [
39112         "Turks and Caicos Islands",
39113         "tc",
39114         "1649"
39115       ],
39116       [
39117         "Tuvalu",
39118         "tv",
39119         "688"
39120       ],
39121       [
39122         "U.S. Virgin Islands",
39123         "vi",
39124         "1340"
39125       ],
39126       [
39127         "Uganda",
39128         "ug",
39129         "256"
39130       ],
39131       [
39132         "Ukraine (Україна)",
39133         "ua",
39134         "380"
39135       ],
39136       [
39137         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39138         "ae",
39139         "971"
39140       ],
39141       [
39142         "United Kingdom",
39143         "gb",
39144         "44",
39145         0
39146       ],
39147       [
39148         "United States",
39149         "us",
39150         "1",
39151         0
39152       ],
39153       [
39154         "Uruguay",
39155         "uy",
39156         "598"
39157       ],
39158       [
39159         "Uzbekistan (Oʻzbekiston)",
39160         "uz",
39161         "998"
39162       ],
39163       [
39164         "Vanuatu",
39165         "vu",
39166         "678"
39167       ],
39168       [
39169         "Vatican City (Città del Vaticano)",
39170         "va",
39171         "39",
39172         1
39173       ],
39174       [
39175         "Venezuela",
39176         "ve",
39177         "58"
39178       ],
39179       [
39180         "Vietnam (Việt Nam)",
39181         "vn",
39182         "84"
39183       ],
39184       [
39185         "Wallis and Futuna (Wallis-et-Futuna)",
39186         "wf",
39187         "681"
39188       ],
39189       [
39190         "Western Sahara (‫الصحراء الغربية‬‎)",
39191         "eh",
39192         "212",
39193         1
39194       ],
39195       [
39196         "Yemen (‫اليمن‬‎)",
39197         "ye",
39198         "967"
39199       ],
39200       [
39201         "Zambia",
39202         "zm",
39203         "260"
39204       ],
39205       [
39206         "Zimbabwe",
39207         "zw",
39208         "263"
39209       ],
39210       [
39211         "Åland Islands",
39212         "ax",
39213         "358",
39214         1
39215       ]
39216   ];
39217   
39218   return d;
39219 }/**
39220 *    This script refer to:
39221 *    Title: International Telephone Input
39222 *    Author: Jack O'Connor
39223 *    Code version:  v12.1.12
39224 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39225 **/
39226
39227 /**
39228  * @class Roo.bootstrap.PhoneInput
39229  * @extends Roo.bootstrap.TriggerField
39230  * An input with International dial-code selection
39231  
39232  * @cfg {String} defaultDialCode default '+852'
39233  * @cfg {Array} preferedCountries default []
39234   
39235  * @constructor
39236  * Create a new PhoneInput.
39237  * @param {Object} config Configuration options
39238  */
39239
39240 Roo.bootstrap.PhoneInput = function(config) {
39241     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39242 };
39243
39244 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39245         
39246         listWidth: undefined,
39247         
39248         selectedClass: 'active',
39249         
39250         invalidClass : "has-warning",
39251         
39252         validClass: 'has-success',
39253         
39254         allowed: '0123456789',
39255         
39256         /**
39257          * @cfg {String} defaultDialCode The default dial code when initializing the input
39258          */
39259         defaultDialCode: '+852',
39260         
39261         /**
39262          * @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
39263          */
39264         preferedCountries: false,
39265         
39266         getAutoCreate : function()
39267         {
39268             var data = Roo.bootstrap.PhoneInputData();
39269             var align = this.labelAlign || this.parentLabelAlign();
39270             var id = Roo.id();
39271             
39272             this.allCountries = [];
39273             this.dialCodeMapping = [];
39274             
39275             for (var i = 0; i < data.length; i++) {
39276               var c = data[i];
39277               this.allCountries[i] = {
39278                 name: c[0],
39279                 iso2: c[1],
39280                 dialCode: c[2],
39281                 priority: c[3] || 0,
39282                 areaCodes: c[4] || null
39283               };
39284               this.dialCodeMapping[c[2]] = {
39285                   name: c[0],
39286                   iso2: c[1],
39287                   priority: c[3] || 0,
39288                   areaCodes: c[4] || null
39289               };
39290             }
39291             
39292             var cfg = {
39293                 cls: 'form-group',
39294                 cn: []
39295             };
39296             
39297             var input =  {
39298                 tag: 'input',
39299                 id : id,
39300                 cls : 'form-control tel-input',
39301                 autocomplete: 'new-password'
39302             };
39303             
39304             var hiddenInput = {
39305                 tag: 'input',
39306                 type: 'hidden',
39307                 cls: 'hidden-tel-input'
39308             };
39309             
39310             if (this.name) {
39311                 hiddenInput.name = this.name;
39312             }
39313             
39314             if (this.disabled) {
39315                 input.disabled = true;
39316             }
39317             
39318             var flag_container = {
39319                 tag: 'div',
39320                 cls: 'flag-box',
39321                 cn: [
39322                     {
39323                         tag: 'div',
39324                         cls: 'flag'
39325                     },
39326                     {
39327                         tag: 'div',
39328                         cls: 'caret'
39329                     }
39330                 ]
39331             };
39332             
39333             var box = {
39334                 tag: 'div',
39335                 cls: this.hasFeedback ? 'has-feedback' : '',
39336                 cn: [
39337                     hiddenInput,
39338                     input,
39339                     {
39340                         tag: 'input',
39341                         cls: 'dial-code-holder',
39342                         disabled: true
39343                     }
39344                 ]
39345             };
39346             
39347             var container = {
39348                 cls: 'roo-select2-container input-group',
39349                 cn: [
39350                     flag_container,
39351                     box
39352                 ]
39353             };
39354             
39355             if (this.fieldLabel.length) {
39356                 var indicator = {
39357                     tag: 'i',
39358                     tooltip: 'This field is required'
39359                 };
39360                 
39361                 var label = {
39362                     tag: 'label',
39363                     'for':  id,
39364                     cls: 'control-label',
39365                     cn: []
39366                 };
39367                 
39368                 var label_text = {
39369                     tag: 'span',
39370                     html: this.fieldLabel
39371                 };
39372                 
39373                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39374                 label.cn = [
39375                     indicator,
39376                     label_text
39377                 ];
39378                 
39379                 if(this.indicatorpos == 'right') {
39380                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39381                     label.cn = [
39382                         label_text,
39383                         indicator
39384                     ];
39385                 }
39386                 
39387                 if(align == 'left') {
39388                     container = {
39389                         tag: 'div',
39390                         cn: [
39391                             container
39392                         ]
39393                     };
39394                     
39395                     if(this.labelWidth > 12){
39396                         label.style = "width: " + this.labelWidth + 'px';
39397                     }
39398                     if(this.labelWidth < 13 && this.labelmd == 0){
39399                         this.labelmd = this.labelWidth;
39400                     }
39401                     if(this.labellg > 0){
39402                         label.cls += ' col-lg-' + this.labellg;
39403                         input.cls += ' col-lg-' + (12 - this.labellg);
39404                     }
39405                     if(this.labelmd > 0){
39406                         label.cls += ' col-md-' + this.labelmd;
39407                         container.cls += ' col-md-' + (12 - this.labelmd);
39408                     }
39409                     if(this.labelsm > 0){
39410                         label.cls += ' col-sm-' + this.labelsm;
39411                         container.cls += ' col-sm-' + (12 - this.labelsm);
39412                     }
39413                     if(this.labelxs > 0){
39414                         label.cls += ' col-xs-' + this.labelxs;
39415                         container.cls += ' col-xs-' + (12 - this.labelxs);
39416                     }
39417                 }
39418             }
39419             
39420             cfg.cn = [
39421                 label,
39422                 container
39423             ];
39424             
39425             var settings = this;
39426             
39427             ['xs','sm','md','lg'].map(function(size){
39428                 if (settings[size]) {
39429                     cfg.cls += ' col-' + size + '-' + settings[size];
39430                 }
39431             });
39432             
39433             this.store = new Roo.data.Store({
39434                 proxy : new Roo.data.MemoryProxy({}),
39435                 reader : new Roo.data.JsonReader({
39436                     fields : [
39437                         {
39438                             'name' : 'name',
39439                             'type' : 'string'
39440                         },
39441                         {
39442                             'name' : 'iso2',
39443                             'type' : 'string'
39444                         },
39445                         {
39446                             'name' : 'dialCode',
39447                             'type' : 'string'
39448                         },
39449                         {
39450                             'name' : 'priority',
39451                             'type' : 'string'
39452                         },
39453                         {
39454                             'name' : 'areaCodes',
39455                             'type' : 'string'
39456                         }
39457                     ]
39458                 })
39459             });
39460             
39461             if(!this.preferedCountries) {
39462                 this.preferedCountries = [
39463                     'hk',
39464                     'gb',
39465                     'us'
39466                 ];
39467             }
39468             
39469             var p = this.preferedCountries.reverse();
39470             
39471             if(p) {
39472                 for (var i = 0; i < p.length; i++) {
39473                     for (var j = 0; j < this.allCountries.length; j++) {
39474                         if(this.allCountries[j].iso2 == p[i]) {
39475                             var t = this.allCountries[j];
39476                             this.allCountries.splice(j,1);
39477                             this.allCountries.unshift(t);
39478                         }
39479                     } 
39480                 }
39481             }
39482             
39483             this.store.proxy.data = {
39484                 success: true,
39485                 data: this.allCountries
39486             };
39487             
39488             return cfg;
39489         },
39490         
39491         initEvents : function()
39492         {
39493             this.createList();
39494             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39495             
39496             this.indicator = this.indicatorEl();
39497             this.flag = this.flagEl();
39498             this.dialCodeHolder = this.dialCodeHolderEl();
39499             
39500             this.trigger = this.el.select('div.flag-box',true).first();
39501             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39502             
39503             var _this = this;
39504             
39505             (function(){
39506                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39507                 _this.list.setWidth(lw);
39508             }).defer(100);
39509             
39510             this.list.on('mouseover', this.onViewOver, this);
39511             this.list.on('mousemove', this.onViewMove, this);
39512             this.inputEl().on("keyup", this.onKeyUp, this);
39513             
39514             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39515
39516             this.view = new Roo.View(this.list, this.tpl, {
39517                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39518             });
39519             
39520             this.view.on('click', this.onViewClick, this);
39521             this.setValue(this.defaultDialCode);
39522         },
39523         
39524         onTriggerClick : function(e)
39525         {
39526             Roo.log('trigger click');
39527             if(this.disabled){
39528                 return;
39529             }
39530             
39531             if(this.isExpanded()){
39532                 this.collapse();
39533                 this.hasFocus = false;
39534             }else {
39535                 this.store.load({});
39536                 this.hasFocus = true;
39537                 this.expand();
39538             }
39539         },
39540         
39541         isExpanded : function()
39542         {
39543             return this.list.isVisible();
39544         },
39545         
39546         collapse : function()
39547         {
39548             if(!this.isExpanded()){
39549                 return;
39550             }
39551             this.list.hide();
39552             Roo.get(document).un('mousedown', this.collapseIf, this);
39553             Roo.get(document).un('mousewheel', this.collapseIf, this);
39554             this.fireEvent('collapse', this);
39555             this.validate();
39556         },
39557         
39558         expand : function()
39559         {
39560             Roo.log('expand');
39561
39562             if(this.isExpanded() || !this.hasFocus){
39563                 return;
39564             }
39565             
39566             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39567             this.list.setWidth(lw);
39568             
39569             this.list.show();
39570             this.restrictHeight();
39571             
39572             Roo.get(document).on('mousedown', this.collapseIf, this);
39573             Roo.get(document).on('mousewheel', this.collapseIf, this);
39574             
39575             this.fireEvent('expand', this);
39576         },
39577         
39578         restrictHeight : function()
39579         {
39580             this.list.alignTo(this.inputEl(), this.listAlign);
39581             this.list.alignTo(this.inputEl(), this.listAlign);
39582         },
39583         
39584         onViewOver : function(e, t)
39585         {
39586             if(this.inKeyMode){
39587                 return;
39588             }
39589             var item = this.view.findItemFromChild(t);
39590             
39591             if(item){
39592                 var index = this.view.indexOf(item);
39593                 this.select(index, false);
39594             }
39595         },
39596
39597         // private
39598         onViewClick : function(view, doFocus, el, e)
39599         {
39600             var index = this.view.getSelectedIndexes()[0];
39601             
39602             var r = this.store.getAt(index);
39603             
39604             if(r){
39605                 this.onSelect(r, index);
39606             }
39607             if(doFocus !== false && !this.blockFocus){
39608                 this.inputEl().focus();
39609             }
39610         },
39611         
39612         onViewMove : function(e, t)
39613         {
39614             this.inKeyMode = false;
39615         },
39616         
39617         select : function(index, scrollIntoView)
39618         {
39619             this.selectedIndex = index;
39620             this.view.select(index);
39621             if(scrollIntoView !== false){
39622                 var el = this.view.getNode(index);
39623                 if(el){
39624                     this.list.scrollChildIntoView(el, false);
39625                 }
39626             }
39627         },
39628         
39629         createList : function()
39630         {
39631             this.list = Roo.get(document.body).createChild({
39632                 tag: 'ul',
39633                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39634                 style: 'display:none'
39635             });
39636             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39637         },
39638         
39639         collapseIf : function(e)
39640         {
39641             var in_combo  = e.within(this.el);
39642             var in_list =  e.within(this.list);
39643             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39644             
39645             if (in_combo || in_list || is_list) {
39646                 return;
39647             }
39648             this.collapse();
39649         },
39650         
39651         onSelect : function(record, index)
39652         {
39653             if(this.fireEvent('beforeselect', this, record, index) !== false){
39654                 
39655                 this.setFlagClass(record.data.iso2);
39656                 this.setDialCode(record.data.dialCode);
39657                 this.hasFocus = false;
39658                 this.collapse();
39659                 this.fireEvent('select', this, record, index);
39660             }
39661         },
39662         
39663         flagEl : function()
39664         {
39665             var flag = this.el.select('div.flag',true).first();
39666             if(!flag){
39667                 return false;
39668             }
39669             return flag;
39670         },
39671         
39672         dialCodeHolderEl : function()
39673         {
39674             var d = this.el.select('input.dial-code-holder',true).first();
39675             if(!d){
39676                 return false;
39677             }
39678             return d;
39679         },
39680         
39681         setDialCode : function(v)
39682         {
39683             this.dialCodeHolder.dom.value = '+'+v;
39684         },
39685         
39686         setFlagClass : function(n)
39687         {
39688             this.flag.dom.className = 'flag '+n;
39689         },
39690         
39691         getValue : function()
39692         {
39693             var v = this.inputEl().getValue();
39694             if(this.dialCodeHolder) {
39695                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39696             }
39697             return v;
39698         },
39699         
39700         setValue : function(v)
39701         {
39702             var d = this.getDialCode(v);
39703             
39704             //invalid dial code
39705             if(v.length == 0 || !d || d.length == 0) {
39706                 if(this.rendered){
39707                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39708                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39709                 }
39710                 return;
39711             }
39712             
39713             //valid dial code
39714             this.setFlagClass(this.dialCodeMapping[d].iso2);
39715             this.setDialCode(d);
39716             this.inputEl().dom.value = v.replace('+'+d,'');
39717             this.hiddenEl().dom.value = this.getValue();
39718             
39719             this.validate();
39720         },
39721         
39722         getDialCode : function(v = '')
39723         {
39724             if (v.length == 0) {
39725                 return this.dialCodeHolder.dom.value;
39726             }
39727             
39728             var dialCode = "";
39729             if (v.charAt(0) != "+") {
39730                 return false;
39731             }
39732             var numericChars = "";
39733             for (var i = 1; i < v.length; i++) {
39734               var c = v.charAt(i);
39735               if (!isNaN(c)) {
39736                 numericChars += c;
39737                 if (this.dialCodeMapping[numericChars]) {
39738                   dialCode = v.substr(1, i);
39739                 }
39740                 if (numericChars.length == 4) {
39741                   break;
39742                 }
39743               }
39744             }
39745             return dialCode;
39746         },
39747         
39748         reset : function()
39749         {
39750             this.setValue(this.defaultDialCode);
39751             this.validate();
39752         },
39753         
39754         hiddenEl : function()
39755         {
39756             return this.el.select('input.hidden-tel-input',true).first();
39757         },
39758         
39759         onKeyUp : function(e){
39760             
39761             var k = e.getKey();
39762             var c = e.getCharCode();
39763             
39764             if(
39765                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39766                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39767             ){
39768                 e.stopEvent();
39769             }
39770             
39771             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39772             //     return;
39773             // }
39774             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39775                 e.stopEvent();
39776             }
39777             
39778             this.setValue(this.getValue());
39779         }
39780         
39781 });
39782 /**
39783  * @class Roo.bootstrap.MoneyField
39784  * @extends Roo.bootstrap.ComboBox
39785  * Bootstrap MoneyField class
39786  * 
39787  * @constructor
39788  * Create a new MoneyField.
39789  * @param {Object} config Configuration options
39790  */
39791
39792 Roo.bootstrap.MoneyField = function(config) {
39793     
39794     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39795     
39796 };
39797
39798 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39799     
39800     /**
39801      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39802      */
39803     allowDecimals : true,
39804     /**
39805      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39806      */
39807     decimalSeparator : ".",
39808     /**
39809      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39810      */
39811     decimalPrecision : 2,
39812     /**
39813      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39814      */
39815     allowNegative : true,
39816     /**
39817      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39818      */
39819     minValue : Number.NEGATIVE_INFINITY,
39820     /**
39821      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39822      */
39823     maxValue : Number.MAX_VALUE,
39824     /**
39825      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39826      */
39827     minText : "The minimum value for this field is {0}",
39828     /**
39829      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39830      */
39831     maxText : "The maximum value for this field is {0}",
39832     /**
39833      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39834      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39835      */
39836     nanText : "{0} is not a valid number",
39837     /**
39838      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39839      */
39840     castInt : true,
39841     
39842     inputlg : 9,
39843     inputmd : 9,
39844     inputsm : 9,
39845     inputxs : 6,
39846     
39847     store : false,
39848     
39849     getAutoCreate : function()
39850     {
39851         var align = this.labelAlign || this.parentLabelAlign();
39852         
39853         var id = Roo.id();
39854
39855         var cfg = {
39856             cls: 'form-group',
39857             cn: []
39858         };
39859
39860         var input =  {
39861             tag: 'input',
39862             id : id,
39863             cls : 'form-control roo-money-amount-input',
39864             autocomplete: 'new-password'
39865         };
39866         
39867         if (this.name) {
39868             input.name = this.name;
39869         }
39870
39871         if (this.disabled) {
39872             input.disabled = true;
39873         }
39874
39875         var clg = 12 - this.inputlg;
39876         var cmd = 12 - this.inputmd;
39877         var csm = 12 - this.inputsm;
39878         var cxs = 12 - this.inputxs;
39879         
39880         var container = {
39881             tag : 'div',
39882             cls : 'row roo-money-field',
39883             cn : [
39884                 {
39885                     tag : 'div',
39886                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39887                     cn : [
39888                         {
39889                             tag : 'div',
39890                             cls: 'roo-select2-container input-group',
39891                             cn: [
39892                                 {
39893                                     tag : 'input',
39894                                     cls : 'form-control roo-money-currency-input',
39895                                     autocomplete: 'new-password'
39896                                 },
39897                                 {
39898                                     tag :'span',
39899                                     cls : 'input-group-addon',
39900                                     cn : [
39901                                         {
39902                                             tag: 'span',
39903                                             cls: 'caret'
39904                                         }
39905                                     ]
39906                                 }
39907                             ]
39908                         }
39909                     ]
39910                 },
39911                 {
39912                     tag : 'div',
39913                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
39914                     cn : [
39915                         {
39916                             tag: 'div',
39917                             cls: this.hasFeedback ? 'has-feedback' : '',
39918                             cn: [
39919                                 input
39920                             ]
39921                         }
39922                     ]
39923                 }
39924             ]
39925             
39926         };
39927         
39928         if (this.fieldLabel.length) {
39929             var indicator = {
39930                 tag: 'i',
39931                 tooltip: 'This field is required'
39932             };
39933
39934             var label = {
39935                 tag: 'label',
39936                 'for':  id,
39937                 cls: 'control-label',
39938                 cn: []
39939             };
39940
39941             var label_text = {
39942                 tag: 'span',
39943                 html: this.fieldLabel
39944             };
39945
39946             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39947             label.cn = [
39948                 indicator,
39949                 label_text
39950             ];
39951
39952             if(this.indicatorpos == 'right') {
39953                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39954                 label.cn = [
39955                     label_text,
39956                     indicator
39957                 ];
39958             }
39959
39960             if(align == 'left') {
39961                 container = {
39962                     tag: 'div',
39963                     cn: [
39964                         container
39965                     ]
39966                 };
39967
39968                 if(this.labelWidth > 12){
39969                     label.style = "width: " + this.labelWidth + 'px';
39970                 }
39971                 if(this.labelWidth < 13 && this.labelmd == 0){
39972                     this.labelmd = this.labelWidth;
39973                 }
39974                 if(this.labellg > 0){
39975                     label.cls += ' col-lg-' + this.labellg;
39976                     input.cls += ' col-lg-' + (12 - this.labellg);
39977                 }
39978                 if(this.labelmd > 0){
39979                     label.cls += ' col-md-' + this.labelmd;
39980                     container.cls += ' col-md-' + (12 - this.labelmd);
39981                 }
39982                 if(this.labelsm > 0){
39983                     label.cls += ' col-sm-' + this.labelsm;
39984                     container.cls += ' col-sm-' + (12 - this.labelsm);
39985                 }
39986                 if(this.labelxs > 0){
39987                     label.cls += ' col-xs-' + this.labelxs;
39988                     container.cls += ' col-xs-' + (12 - this.labelxs);
39989                 }
39990             }
39991         }
39992
39993         cfg.cn = [
39994             label,
39995             container
39996         ];
39997
39998         var settings = this;
39999
40000         ['xs','sm','md','lg'].map(function(size){
40001             if (settings[size]) {
40002                 cfg.cls += ' col-' + size + '-' + settings[size];
40003             }
40004         });
40005         
40006         return cfg;
40007         
40008     },
40009     
40010     initEvents : function()
40011     {
40012         this.indicator = this.indicatorEl();
40013         
40014         this.initCurrencyEvent();
40015         
40016         this.initNumberEvent();
40017         
40018     },
40019     
40020     initCurrencyEvent : function()
40021     {
40022         if (!this.store) {
40023             throw "can not find store for combo";
40024         }
40025         
40026         this.store = Roo.factory(this.store, Roo.data);
40027         this.store.parent = this;
40028         
40029         this.createList();
40030         
40031         this.triggerEl = this.el.select('.input-group-addon', true).first();
40032         
40033         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40034         
40035         var _this = this;
40036         
40037         (function(){
40038             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40039             _this.list.setWidth(lw);
40040         }).defer(100);
40041         
40042         this.list.on('mouseover', this.onViewOver, this);
40043         this.list.on('mousemove', this.onViewMove, this);
40044         this.list.on('scroll', this.onViewScroll, this);
40045         
40046         if(!this.tpl){
40047             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40048         }
40049         
40050         this.view = new Roo.View(this.list, this.tpl, {
40051             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40052         });
40053         
40054         this.view.on('click', this.onViewClick, this);
40055         
40056         this.store.on('beforeload', this.onBeforeLoad, this);
40057         this.store.on('load', this.onLoad, this);
40058         this.store.on('loadexception', this.onLoadException, this);
40059         
40060         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40061             "up" : function(e){
40062                 this.inKeyMode = true;
40063                 this.selectPrev();
40064             },
40065
40066             "down" : function(e){
40067                 if(!this.isExpanded()){
40068                     this.onTriggerClick();
40069                 }else{
40070                     this.inKeyMode = true;
40071                     this.selectNext();
40072                 }
40073             },
40074
40075             "enter" : function(e){
40076                 this.collapse();
40077                 
40078                 if(this.fireEvent("specialkey", this, e)){
40079                     this.onViewClick(false);
40080                 }
40081                 
40082                 return true;
40083             },
40084
40085             "esc" : function(e){
40086                 this.collapse();
40087             },
40088
40089             "tab" : function(e){
40090                 this.collapse();
40091                 
40092                 if(this.fireEvent("specialkey", this, e)){
40093                     this.onViewClick(false);
40094                 }
40095                 
40096                 return true;
40097             },
40098
40099             scope : this,
40100
40101             doRelay : function(foo, bar, hname){
40102                 if(hname == 'down' || this.scope.isExpanded()){
40103                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40104                 }
40105                 return true;
40106             },
40107
40108             forceKeyDown: true
40109         });
40110         
40111         this.queryDelay = Math.max(this.queryDelay || 10,
40112                 this.mode == 'local' ? 10 : 250);
40113         
40114         
40115         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
40116         
40117         if(this.typeAhead){
40118             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
40119         }
40120         
40121         this.currencyEl().on("keyup", this.onCurrencyKeyUp, this);
40122         
40123     },
40124     
40125     initNumberEvent : function(e)
40126     {
40127         this.inputEl().on("keydown" , this.fireKey,  this);
40128         this.inputEl().on("focus", this.onFocus,  this);
40129         this.inputEl().on("blur", this.onBlur,  this);
40130         
40131         this.inputEl().relayEvent('keyup', this);
40132         
40133         if(this.indicator){
40134             this.indicator.addClass('invisible');
40135         }
40136  
40137         this.originalValue = this.getValue();
40138         
40139         if(this.validationEvent == 'keyup'){
40140             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40141             this.inputEl().on('keyup', this.filterValidation, this);
40142         }
40143         else if(this.validationEvent !== false){
40144             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40145         }
40146         
40147         if(this.selectOnFocus){
40148             this.on("focus", this.preFocus, this);
40149             
40150         }
40151         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40152             this.inputEl().on("keypress", this.filterKeys, this);
40153         } else {
40154             this.inputEl().relayEvent('keypress', this);
40155         }
40156         
40157         var allowed = "0123456789";
40158         
40159         if(this.allowDecimals){
40160             allowed += this.decimalSeparator;
40161         }
40162         
40163         if(this.allowNegative){
40164             allowed += "-";
40165         }
40166         
40167         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40168         
40169         var keyPress = function(e){
40170             
40171             var k = e.getKey();
40172             
40173             var c = e.getCharCode();
40174             
40175             if(
40176                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40177                     allowed.indexOf(String.fromCharCode(c)) === -1
40178             ){
40179                 e.stopEvent();
40180                 return;
40181             }
40182             
40183             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40184                 return;
40185             }
40186             
40187             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40188                 e.stopEvent();
40189             }
40190         };
40191         
40192         this.inputEl().on("keypress", keyPress, this);
40193         
40194     },
40195     
40196     onTriggerClick : function(e)
40197     {   
40198         if(this.disabled){
40199             return;
40200         }
40201         
40202         this.page = 0;
40203         this.loadNext = false;
40204         
40205         if(this.isExpanded()){
40206             this.collapse();
40207             return;
40208         }
40209         
40210         this.hasFocus = true;
40211         
40212         if(this.triggerAction == 'all') {
40213             this.doQuery(this.allQuery, true);
40214             return;
40215         }
40216         
40217         this.doQuery(this.getCurrency());
40218     },
40219     
40220     getCurrency : function()
40221     {   
40222         var v = this.currencyEl().getValue();
40223         
40224         return v;
40225     },
40226     
40227     restrictHeight : function()
40228     {
40229         this.list.alignTo(this.currencyEl(), this.listAlign);
40230         this.list.alignTo(this.currencyEl(), this.listAlign);
40231     },
40232     
40233     onViewClick : function(view, doFocus, el, e)
40234     {
40235         var index = this.view.getSelectedIndexes()[0];
40236         
40237         var r = this.store.getAt(index);
40238         
40239         if(r){
40240             this.onSelect(r, index);
40241         }
40242     },
40243     
40244     onSelect : function(record, index){
40245         
40246         if(this.fireEvent('beforeselect', this, record, index) !== false){
40247         
40248             this.setFromCurrencyData(index > -1 ? record.data : false);
40249             
40250             this.collapse();
40251             
40252             this.fireEvent('select', this, record, index);
40253         }
40254     },
40255     
40256     setFromCurrencyData : function(o)
40257     {
40258         var currency = '';
40259         
40260         this.lastCurrency = o;
40261         
40262         if (this.currencyField) {
40263             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40264         } else {
40265             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40266         }
40267         
40268         this.lastSelectionText = currency;
40269         
40270         this.setCurrency(currency);
40271     },
40272     
40273     setFromData : function(o)
40274     {
40275         var c = {};
40276         
40277         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40278         
40279         this.setFromCurrencyData(c);
40280         
40281         var value = '';
40282         
40283         if (this.name) {
40284             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40285         } else {
40286             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40287         }
40288         
40289         this.setValue(value);
40290         
40291     },
40292     
40293     setCurrency : function(v)
40294     {   
40295         this.currencyValue = v;
40296         
40297         if(this.rendered){
40298             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40299             this.validate();
40300         }
40301     },
40302     
40303     setValue : function(v)
40304     {
40305         v = this.fixPrecision(v);
40306         
40307         v = String(v).replace(".", this.decimalSeparator);
40308         
40309         this.value = v;
40310         
40311         if(this.rendered){
40312             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40313             this.validate();
40314         }
40315     },
40316     
40317     getRawValue : function()
40318     {
40319         var v = this.inputEl().getValue();
40320         
40321         return v;
40322     },
40323     
40324     getValue : function()
40325     {
40326         return this.fixPrecision(this.parseValue(this.getRawValue()));
40327     },
40328     
40329     parseValue : function(value)
40330     {
40331         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40332         return isNaN(value) ? '' : value;
40333     },
40334     
40335     fixPrecision : function(value)
40336     {
40337         var nan = isNaN(value);
40338         
40339         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40340             return nan ? '' : value;
40341         }
40342         
40343         return parseFloat(value).toFixed(this.decimalPrecision);
40344     },
40345     
40346     decimalPrecisionFcn : function(v)
40347     {
40348         return Math.floor(v);
40349     },
40350     
40351     validateValue : function(value)
40352     {
40353         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40354             return false;
40355         }
40356         
40357         var num = this.parseValue(value);
40358         
40359         if(isNaN(num)){
40360             this.markInvalid(String.format(this.nanText, value));
40361             return false;
40362         }
40363         
40364         if(num < this.minValue){
40365             this.markInvalid(String.format(this.minText, this.minValue));
40366             return false;
40367         }
40368         
40369         if(num > this.maxValue){
40370             this.markInvalid(String.format(this.maxText, this.maxValue));
40371             return false;
40372         }
40373         
40374         return true;
40375     },
40376     
40377     validate : function()
40378     {
40379         if(this.disabled){
40380             this.markValid();
40381             return true;
40382         }
40383         
40384         var currency = this.getCurrency();
40385         
40386         if(this.validateValue(this.getRawValue()) && currency.length){
40387             this.markValid();
40388             return true;
40389         }
40390         
40391         this.markInvalid();
40392         return false;
40393     },
40394     
40395     getName: function()
40396     {
40397         return this.name;
40398     },
40399     
40400     beforeBlur : function()
40401     {
40402         if(!this.castInt){
40403             return;
40404         }
40405         
40406         var v = this.parseValue(this.getRawValue());
40407         
40408         if(v){
40409             this.setValue(v);
40410         }
40411     },
40412     
40413     onBlur : function()
40414     {
40415         this.beforeBlur();
40416         
40417         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40418             //this.el.removeClass(this.focusClass);
40419         }
40420         
40421         this.hasFocus = false;
40422         
40423         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40424             this.validate();
40425         }
40426         
40427         var v = this.getValue();
40428         
40429         if(String(v) !== String(this.startValue)){
40430             this.fireEvent('change', this, v, this.startValue);
40431         }
40432         
40433         this.fireEvent("blur", this);
40434     },
40435     
40436     onCurrencyKeyUp : function(e)
40437     {
40438         Roo.log('on currency keyup');
40439         if(!e.isSpecialKey()){
40440             this.lastKey = e.getKey();
40441             this.dqTask.delay(this.queryDelay);
40442         }
40443     },
40444     
40445     inputEl : function()
40446     {
40447         return this.el.select('.roo-money-amount-input', true).first();
40448     },
40449     
40450     currencyEl : function()
40451     {
40452         return this.el.select('.roo-money-currency-input', true).first();
40453     },
40454     
40455     initQuery : function()
40456     {
40457         var v = this.getCurrency();
40458         Roo.log('initQuery???');
40459         this.doQuery(v);
40460     },
40461     
40462     onTypeAhead : function()
40463     {
40464         if(this.store.getCount() > 0){
40465             var r = this.store.getAt(0);
40466             var newValue = r.data[this.currencyField];
40467             var len = newValue.length;
40468             var selStart = this.getCurrency().length;
40469             
40470             if(selStart != len){
40471                 this.setCurrency(newValue);
40472                 this.selectText(selStart, newValue.length);
40473             }
40474         }
40475     },
40476     
40477     selectText : function(start, end)
40478     {
40479         var v = this.getCurrency();
40480         
40481         if(v.length > 0){
40482             start = start === undefined ? 0 : start;
40483             end = end === undefined ? v.length : end;
40484             var d = this.el.dom;
40485             if(d.setSelectionRange){
40486                 d.setSelectionRange(start, end);
40487             }else if(d.createTextRange){
40488                 var range = d.createTextRange();
40489                 range.moveStart("character", start);
40490                 range.moveEnd("character", v.length-end);
40491                 range.select();
40492             }
40493         }
40494     }
40495     
40496 });