less/roojs-bootstrap/modal.less
[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 (default|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 ? thd.getHeight() : 0) + (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     /**
7622      * @cfg {Boolean} maskBody
7623      */
7624     maskBody : false,
7625
7626     getAutoCreate : function(){
7627
7628         var cfg = {
7629             tag: 'form',
7630             method : this.method || 'POST',
7631             id : this.id || Roo.id(),
7632             cls : ''
7633         };
7634         if (this.parent().xtype.match(/^Nav/)) {
7635             cfg.cls = 'navbar-form navbar-' + this.align;
7636
7637         }
7638
7639         if (this.labelAlign == 'left' ) {
7640             cfg.cls += ' form-horizontal';
7641         }
7642
7643
7644         return cfg;
7645     },
7646     initEvents : function()
7647     {
7648         this.el.on('submit', this.onSubmit, this);
7649         // this was added as random key presses on the form where triggering form submit.
7650         this.el.on('keypress', function(e) {
7651             if (e.getCharCode() != 13) {
7652                 return true;
7653             }
7654             // we might need to allow it for textareas.. and some other items.
7655             // check e.getTarget().
7656
7657             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7658                 return true;
7659             }
7660
7661             Roo.log("keypress blocked");
7662
7663             e.preventDefault();
7664             return false;
7665         });
7666         
7667     },
7668     // private
7669     onSubmit : function(e){
7670         e.stopEvent();
7671     },
7672
7673      /**
7674      * Returns true if client-side validation on the form is successful.
7675      * @return Boolean
7676      */
7677     isValid : function(){
7678         var items = this.getItems();
7679         var valid = true;
7680         var target = false;
7681         
7682         items.each(function(f){
7683             if(f.validate()){
7684                 return;
7685             }
7686             valid = false;
7687
7688             if(!target && f.el.isVisible(true)){
7689                 target = f;
7690             }
7691            
7692         });
7693         
7694         if(this.errorMask && !valid){
7695             Roo.bootstrap.Form.popover.mask(this, target);
7696         }
7697         
7698         return valid;
7699     },
7700     
7701     /**
7702      * Returns true if any fields in this form have changed since their original load.
7703      * @return Boolean
7704      */
7705     isDirty : function(){
7706         var dirty = false;
7707         var items = this.getItems();
7708         items.each(function(f){
7709            if(f.isDirty()){
7710                dirty = true;
7711                return false;
7712            }
7713            return true;
7714         });
7715         return dirty;
7716     },
7717      /**
7718      * Performs a predefined action (submit or load) or custom actions you define on this form.
7719      * @param {String} actionName The name of the action type
7720      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7721      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7722      * accept other config options):
7723      * <pre>
7724 Property          Type             Description
7725 ----------------  ---------------  ----------------------------------------------------------------------------------
7726 url               String           The url for the action (defaults to the form's url)
7727 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7728 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7729 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7730                                    validate the form on the client (defaults to false)
7731      * </pre>
7732      * @return {BasicForm} this
7733      */
7734     doAction : function(action, options){
7735         if(typeof action == 'string'){
7736             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7737         }
7738         if(this.fireEvent('beforeaction', this, action) !== false){
7739             this.beforeAction(action);
7740             action.run.defer(100, action);
7741         }
7742         return this;
7743     },
7744
7745     // private
7746     beforeAction : function(action){
7747         var o = action.options;
7748         
7749         if(this.loadMask){
7750             
7751             if(this.maskBody){
7752                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7753             } else {
7754                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7755             }
7756         }
7757         // not really supported yet.. ??
7758
7759         //if(this.waitMsgTarget === true){
7760         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7761         //}else if(this.waitMsgTarget){
7762         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7763         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7764         //}else {
7765         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7766        // }
7767
7768     },
7769
7770     // private
7771     afterAction : function(action, success){
7772         this.activeAction = null;
7773         var o = action.options;
7774
7775         //if(this.waitMsgTarget === true){
7776             Roo.get(document.body).unmask();
7777             this.el.unmask();
7778         //}else if(this.waitMsgTarget){
7779         //    this.waitMsgTarget.unmask();
7780         //}else{
7781         //    Roo.MessageBox.updateProgress(1);
7782         //    Roo.MessageBox.hide();
7783        // }
7784         //
7785         if(success){
7786             if(o.reset){
7787                 this.reset();
7788             }
7789             Roo.callback(o.success, o.scope, [this, action]);
7790             this.fireEvent('actioncomplete', this, action);
7791
7792         }else{
7793
7794             // failure condition..
7795             // we have a scenario where updates need confirming.
7796             // eg. if a locking scenario exists..
7797             // we look for { errors : { needs_confirm : true }} in the response.
7798             if (
7799                 (typeof(action.result) != 'undefined')  &&
7800                 (typeof(action.result.errors) != 'undefined')  &&
7801                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7802            ){
7803                 var _t = this;
7804                 Roo.log("not supported yet");
7805                  /*
7806
7807                 Roo.MessageBox.confirm(
7808                     "Change requires confirmation",
7809                     action.result.errorMsg,
7810                     function(r) {
7811                         if (r != 'yes') {
7812                             return;
7813                         }
7814                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7815                     }
7816
7817                 );
7818                 */
7819
7820
7821                 return;
7822             }
7823
7824             Roo.callback(o.failure, o.scope, [this, action]);
7825             // show an error message if no failed handler is set..
7826             if (!this.hasListener('actionfailed')) {
7827                 Roo.log("need to add dialog support");
7828                 /*
7829                 Roo.MessageBox.alert("Error",
7830                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7831                         action.result.errorMsg :
7832                         "Saving Failed, please check your entries or try again"
7833                 );
7834                 */
7835             }
7836
7837             this.fireEvent('actionfailed', this, action);
7838         }
7839
7840     },
7841     /**
7842      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7843      * @param {String} id The value to search for
7844      * @return Field
7845      */
7846     findField : function(id){
7847         var items = this.getItems();
7848         var field = items.get(id);
7849         if(!field){
7850              items.each(function(f){
7851                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7852                     field = f;
7853                     return false;
7854                 }
7855                 return true;
7856             });
7857         }
7858         return field || null;
7859     },
7860      /**
7861      * Mark fields in this form invalid in bulk.
7862      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7863      * @return {BasicForm} this
7864      */
7865     markInvalid : function(errors){
7866         if(errors instanceof Array){
7867             for(var i = 0, len = errors.length; i < len; i++){
7868                 var fieldError = errors[i];
7869                 var f = this.findField(fieldError.id);
7870                 if(f){
7871                     f.markInvalid(fieldError.msg);
7872                 }
7873             }
7874         }else{
7875             var field, id;
7876             for(id in errors){
7877                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7878                     field.markInvalid(errors[id]);
7879                 }
7880             }
7881         }
7882         //Roo.each(this.childForms || [], function (f) {
7883         //    f.markInvalid(errors);
7884         //});
7885
7886         return this;
7887     },
7888
7889     /**
7890      * Set values for fields in this form in bulk.
7891      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7892      * @return {BasicForm} this
7893      */
7894     setValues : function(values){
7895         if(values instanceof Array){ // array of objects
7896             for(var i = 0, len = values.length; i < len; i++){
7897                 var v = values[i];
7898                 var f = this.findField(v.id);
7899                 if(f){
7900                     f.setValue(v.value);
7901                     if(this.trackResetOnLoad){
7902                         f.originalValue = f.getValue();
7903                     }
7904                 }
7905             }
7906         }else{ // object hash
7907             var field, id;
7908             for(id in values){
7909                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7910
7911                     if (field.setFromData &&
7912                         field.valueField &&
7913                         field.displayField &&
7914                         // combos' with local stores can
7915                         // be queried via setValue()
7916                         // to set their value..
7917                         (field.store && !field.store.isLocal)
7918                         ) {
7919                         // it's a combo
7920                         var sd = { };
7921                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7922                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7923                         field.setFromData(sd);
7924
7925                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7926                         
7927                         field.setFromData(values);
7928                         
7929                     } else {
7930                         field.setValue(values[id]);
7931                     }
7932
7933
7934                     if(this.trackResetOnLoad){
7935                         field.originalValue = field.getValue();
7936                     }
7937                 }
7938             }
7939         }
7940
7941         //Roo.each(this.childForms || [], function (f) {
7942         //    f.setValues(values);
7943         //});
7944
7945         return this;
7946     },
7947
7948     /**
7949      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7950      * they are returned as an array.
7951      * @param {Boolean} asString
7952      * @return {Object}
7953      */
7954     getValues : function(asString){
7955         //if (this.childForms) {
7956             // copy values from the child forms
7957         //    Roo.each(this.childForms, function (f) {
7958         //        this.setValues(f.getValues());
7959         //    }, this);
7960         //}
7961
7962
7963
7964         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7965         if(asString === true){
7966             return fs;
7967         }
7968         return Roo.urlDecode(fs);
7969     },
7970
7971     /**
7972      * Returns the fields in this form as an object with key/value pairs.
7973      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7974      * @return {Object}
7975      */
7976     getFieldValues : function(with_hidden)
7977     {
7978         var items = this.getItems();
7979         var ret = {};
7980         items.each(function(f){
7981             
7982             if (!f.getName()) {
7983                 return;
7984             }
7985             
7986             var v = f.getValue();
7987             
7988             if (f.inputType =='radio') {
7989                 if (typeof(ret[f.getName()]) == 'undefined') {
7990                     ret[f.getName()] = ''; // empty..
7991                 }
7992
7993                 if (!f.el.dom.checked) {
7994                     return;
7995
7996                 }
7997                 v = f.el.dom.value;
7998
7999             }
8000             
8001             if(f.xtype == 'MoneyField'){
8002                 ret[f.currencyName] = f.getCurrency();
8003             }
8004
8005             // not sure if this supported any more..
8006             if ((typeof(v) == 'object') && f.getRawValue) {
8007                 v = f.getRawValue() ; // dates..
8008             }
8009             // combo boxes where name != hiddenName...
8010             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8011                 ret[f.name] = f.getRawValue();
8012             }
8013             ret[f.getName()] = v;
8014         });
8015
8016         return ret;
8017     },
8018
8019     /**
8020      * Clears all invalid messages in this form.
8021      * @return {BasicForm} this
8022      */
8023     clearInvalid : function(){
8024         var items = this.getItems();
8025
8026         items.each(function(f){
8027            f.clearInvalid();
8028         });
8029
8030
8031
8032         return this;
8033     },
8034
8035     /**
8036      * Resets this form.
8037      * @return {BasicForm} this
8038      */
8039     reset : function(){
8040         var items = this.getItems();
8041         items.each(function(f){
8042             f.reset();
8043         });
8044
8045         Roo.each(this.childForms || [], function (f) {
8046             f.reset();
8047         });
8048
8049
8050         return this;
8051     },
8052     
8053     getItems : function()
8054     {
8055         var r=new Roo.util.MixedCollection(false, function(o){
8056             return o.id || (o.id = Roo.id());
8057         });
8058         var iter = function(el) {
8059             if (el.inputEl) {
8060                 r.add(el);
8061             }
8062             if (!el.items) {
8063                 return;
8064             }
8065             Roo.each(el.items,function(e) {
8066                 iter(e);
8067             });
8068
8069
8070         };
8071
8072         iter(this);
8073         return r;
8074         
8075     }
8076
8077 });
8078
8079 Roo.apply(Roo.bootstrap.Form, {
8080     
8081     popover : {
8082         
8083         padding : 5,
8084         
8085         isApplied : false,
8086         
8087         isMasked : false,
8088         
8089         form : false,
8090         
8091         target : false,
8092         
8093         toolTip : false,
8094         
8095         intervalID : false,
8096         
8097         maskEl : false,
8098         
8099         apply : function()
8100         {
8101             if(this.isApplied){
8102                 return;
8103             }
8104             
8105             this.maskEl = {
8106                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8107                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8108                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8109                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8110             };
8111             
8112             this.maskEl.top.enableDisplayMode("block");
8113             this.maskEl.left.enableDisplayMode("block");
8114             this.maskEl.bottom.enableDisplayMode("block");
8115             this.maskEl.right.enableDisplayMode("block");
8116             
8117             this.toolTip = new Roo.bootstrap.Tooltip({
8118                 cls : 'roo-form-error-popover',
8119                 alignment : {
8120                     'left' : ['r-l', [-2,0], 'right'],
8121                     'right' : ['l-r', [2,0], 'left'],
8122                     'bottom' : ['tl-bl', [0,2], 'top'],
8123                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8124                 }
8125             });
8126             
8127             this.toolTip.render(Roo.get(document.body));
8128
8129             this.toolTip.el.enableDisplayMode("block");
8130             
8131             Roo.get(document.body).on('click', function(){
8132                 this.unmask();
8133             }, this);
8134             
8135             Roo.get(document.body).on('touchstart', function(){
8136                 this.unmask();
8137             }, this);
8138             
8139             this.isApplied = true
8140         },
8141         
8142         mask : function(form, target)
8143         {
8144             this.form = form;
8145             
8146             this.target = target;
8147             
8148             if(!this.form.errorMask || !target.el){
8149                 return;
8150             }
8151             
8152             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8153             
8154             Roo.log(scrollable);
8155             
8156             var ot = this.target.el.calcOffsetsTo(scrollable);
8157             
8158             var scrollTo = ot[1] - this.form.maskOffset;
8159             
8160             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8161             
8162             scrollable.scrollTo('top', scrollTo);
8163             
8164             var box = this.target.el.getBox();
8165             Roo.log(box);
8166             var zIndex = Roo.bootstrap.Modal.zIndex++;
8167
8168             
8169             this.maskEl.top.setStyle('position', 'absolute');
8170             this.maskEl.top.setStyle('z-index', zIndex);
8171             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8172             this.maskEl.top.setLeft(0);
8173             this.maskEl.top.setTop(0);
8174             this.maskEl.top.show();
8175             
8176             this.maskEl.left.setStyle('position', 'absolute');
8177             this.maskEl.left.setStyle('z-index', zIndex);
8178             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8179             this.maskEl.left.setLeft(0);
8180             this.maskEl.left.setTop(box.y - this.padding);
8181             this.maskEl.left.show();
8182
8183             this.maskEl.bottom.setStyle('position', 'absolute');
8184             this.maskEl.bottom.setStyle('z-index', zIndex);
8185             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8186             this.maskEl.bottom.setLeft(0);
8187             this.maskEl.bottom.setTop(box.bottom + this.padding);
8188             this.maskEl.bottom.show();
8189
8190             this.maskEl.right.setStyle('position', 'absolute');
8191             this.maskEl.right.setStyle('z-index', zIndex);
8192             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8193             this.maskEl.right.setLeft(box.right + this.padding);
8194             this.maskEl.right.setTop(box.y - this.padding);
8195             this.maskEl.right.show();
8196
8197             this.toolTip.bindEl = this.target.el;
8198
8199             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8200
8201             var tip = this.target.blankText;
8202
8203             if(this.target.getValue() !== '' ) {
8204                 
8205                 if (this.target.invalidText.length) {
8206                     tip = this.target.invalidText;
8207                 } else if (this.target.regexText.length){
8208                     tip = this.target.regexText;
8209                 }
8210             }
8211
8212             this.toolTip.show(tip);
8213
8214             this.intervalID = window.setInterval(function() {
8215                 Roo.bootstrap.Form.popover.unmask();
8216             }, 10000);
8217
8218             window.onwheel = function(){ return false;};
8219             
8220             (function(){ this.isMasked = true; }).defer(500, this);
8221             
8222         },
8223         
8224         unmask : function()
8225         {
8226             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8227                 return;
8228             }
8229             
8230             this.maskEl.top.setStyle('position', 'absolute');
8231             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8232             this.maskEl.top.hide();
8233
8234             this.maskEl.left.setStyle('position', 'absolute');
8235             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8236             this.maskEl.left.hide();
8237
8238             this.maskEl.bottom.setStyle('position', 'absolute');
8239             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8240             this.maskEl.bottom.hide();
8241
8242             this.maskEl.right.setStyle('position', 'absolute');
8243             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8244             this.maskEl.right.hide();
8245             
8246             this.toolTip.hide();
8247             
8248             this.toolTip.el.hide();
8249             
8250             window.onwheel = function(){ return true;};
8251             
8252             if(this.intervalID){
8253                 window.clearInterval(this.intervalID);
8254                 this.intervalID = false;
8255             }
8256             
8257             this.isMasked = false;
8258             
8259         }
8260         
8261     }
8262     
8263 });
8264
8265 /*
8266  * Based on:
8267  * Ext JS Library 1.1.1
8268  * Copyright(c) 2006-2007, Ext JS, LLC.
8269  *
8270  * Originally Released Under LGPL - original licence link has changed is not relivant.
8271  *
8272  * Fork - LGPL
8273  * <script type="text/javascript">
8274  */
8275 /**
8276  * @class Roo.form.VTypes
8277  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8278  * @singleton
8279  */
8280 Roo.form.VTypes = function(){
8281     // closure these in so they are only created once.
8282     var alpha = /^[a-zA-Z_]+$/;
8283     var alphanum = /^[a-zA-Z0-9_]+$/;
8284     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8285     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8286
8287     // All these messages and functions are configurable
8288     return {
8289         /**
8290          * The function used to validate email addresses
8291          * @param {String} value The email address
8292          */
8293         'email' : function(v){
8294             return email.test(v);
8295         },
8296         /**
8297          * The error text to display when the email validation function returns false
8298          * @type String
8299          */
8300         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8301         /**
8302          * The keystroke filter mask to be applied on email input
8303          * @type RegExp
8304          */
8305         'emailMask' : /[a-z0-9_\.\-@]/i,
8306
8307         /**
8308          * The function used to validate URLs
8309          * @param {String} value The URL
8310          */
8311         'url' : function(v){
8312             return url.test(v);
8313         },
8314         /**
8315          * The error text to display when the url validation function returns false
8316          * @type String
8317          */
8318         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8319         
8320         /**
8321          * The function used to validate alpha values
8322          * @param {String} value The value
8323          */
8324         'alpha' : function(v){
8325             return alpha.test(v);
8326         },
8327         /**
8328          * The error text to display when the alpha validation function returns false
8329          * @type String
8330          */
8331         'alphaText' : 'This field should only contain letters and _',
8332         /**
8333          * The keystroke filter mask to be applied on alpha input
8334          * @type RegExp
8335          */
8336         'alphaMask' : /[a-z_]/i,
8337
8338         /**
8339          * The function used to validate alphanumeric values
8340          * @param {String} value The value
8341          */
8342         'alphanum' : function(v){
8343             return alphanum.test(v);
8344         },
8345         /**
8346          * The error text to display when the alphanumeric validation function returns false
8347          * @type String
8348          */
8349         'alphanumText' : 'This field should only contain letters, numbers and _',
8350         /**
8351          * The keystroke filter mask to be applied on alphanumeric input
8352          * @type RegExp
8353          */
8354         'alphanumMask' : /[a-z0-9_]/i
8355     };
8356 }();/*
8357  * - LGPL
8358  *
8359  * Input
8360  * 
8361  */
8362
8363 /**
8364  * @class Roo.bootstrap.Input
8365  * @extends Roo.bootstrap.Component
8366  * Bootstrap Input class
8367  * @cfg {Boolean} disabled is it disabled
8368  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8369  * @cfg {String} name name of the input
8370  * @cfg {string} fieldLabel - the label associated
8371  * @cfg {string} placeholder - placeholder to put in text.
8372  * @cfg {string}  before - input group add on before
8373  * @cfg {string} after - input group add on after
8374  * @cfg {string} size - (lg|sm) or leave empty..
8375  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8376  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8377  * @cfg {Number} md colspan out of 12 for computer-sized screens
8378  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8379  * @cfg {string} value default value of the input
8380  * @cfg {Number} labelWidth set the width of label 
8381  * @cfg {Number} labellg set the width of label (1-12)
8382  * @cfg {Number} labelmd set the width of label (1-12)
8383  * @cfg {Number} labelsm set the width of label (1-12)
8384  * @cfg {Number} labelxs set the width of label (1-12)
8385  * @cfg {String} labelAlign (top|left)
8386  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8387  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8388  * @cfg {String} indicatorpos (left|right) default left
8389
8390  * @cfg {String} align (left|center|right) Default left
8391  * @cfg {Boolean} forceFeedback (true|false) Default false
8392  * 
8393  * 
8394  * 
8395  * 
8396  * @constructor
8397  * Create a new Input
8398  * @param {Object} config The config object
8399  */
8400
8401 Roo.bootstrap.Input = function(config){
8402     
8403     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8404     
8405     this.addEvents({
8406         /**
8407          * @event focus
8408          * Fires when this field receives input focus.
8409          * @param {Roo.form.Field} this
8410          */
8411         focus : true,
8412         /**
8413          * @event blur
8414          * Fires when this field loses input focus.
8415          * @param {Roo.form.Field} this
8416          */
8417         blur : true,
8418         /**
8419          * @event specialkey
8420          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8421          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8422          * @param {Roo.form.Field} this
8423          * @param {Roo.EventObject} e The event object
8424          */
8425         specialkey : true,
8426         /**
8427          * @event change
8428          * Fires just before the field blurs if the field value has changed.
8429          * @param {Roo.form.Field} this
8430          * @param {Mixed} newValue The new value
8431          * @param {Mixed} oldValue The original value
8432          */
8433         change : true,
8434         /**
8435          * @event invalid
8436          * Fires after the field has been marked as invalid.
8437          * @param {Roo.form.Field} this
8438          * @param {String} msg The validation message
8439          */
8440         invalid : true,
8441         /**
8442          * @event valid
8443          * Fires after the field has been validated with no errors.
8444          * @param {Roo.form.Field} this
8445          */
8446         valid : true,
8447          /**
8448          * @event keyup
8449          * Fires after the key up
8450          * @param {Roo.form.Field} this
8451          * @param {Roo.EventObject}  e The event Object
8452          */
8453         keyup : true
8454     });
8455 };
8456
8457 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8458      /**
8459      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8460       automatic validation (defaults to "keyup").
8461      */
8462     validationEvent : "keyup",
8463      /**
8464      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8465      */
8466     validateOnBlur : true,
8467     /**
8468      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8469      */
8470     validationDelay : 250,
8471      /**
8472      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8473      */
8474     focusClass : "x-form-focus",  // not needed???
8475     
8476        
8477     /**
8478      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8479      */
8480     invalidClass : "has-warning",
8481     
8482     /**
8483      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8484      */
8485     validClass : "has-success",
8486     
8487     /**
8488      * @cfg {Boolean} hasFeedback (true|false) default true
8489      */
8490     hasFeedback : true,
8491     
8492     /**
8493      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8494      */
8495     invalidFeedbackClass : "glyphicon-warning-sign",
8496     
8497     /**
8498      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8499      */
8500     validFeedbackClass : "glyphicon-ok",
8501     
8502     /**
8503      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8504      */
8505     selectOnFocus : false,
8506     
8507      /**
8508      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8509      */
8510     maskRe : null,
8511        /**
8512      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8513      */
8514     vtype : null,
8515     
8516       /**
8517      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8518      */
8519     disableKeyFilter : false,
8520     
8521        /**
8522      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8523      */
8524     disabled : false,
8525      /**
8526      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8527      */
8528     allowBlank : true,
8529     /**
8530      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8531      */
8532     blankText : "Please complete this mandatory field",
8533     
8534      /**
8535      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8536      */
8537     minLength : 0,
8538     /**
8539      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8540      */
8541     maxLength : Number.MAX_VALUE,
8542     /**
8543      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8544      */
8545     minLengthText : "The minimum length for this field is {0}",
8546     /**
8547      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8548      */
8549     maxLengthText : "The maximum length for this field is {0}",
8550   
8551     
8552     /**
8553      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8554      * If available, this function will be called only after the basic validators all return true, and will be passed the
8555      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8556      */
8557     validator : null,
8558     /**
8559      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8560      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8561      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8562      */
8563     regex : null,
8564     /**
8565      * @cfg {String} regexText -- Depricated - use Invalid Text
8566      */
8567     regexText : "",
8568     
8569     /**
8570      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8571      */
8572     invalidText : "",
8573     
8574     
8575     
8576     autocomplete: false,
8577     
8578     
8579     fieldLabel : '',
8580     inputType : 'text',
8581     
8582     name : false,
8583     placeholder: false,
8584     before : false,
8585     after : false,
8586     size : false,
8587     hasFocus : false,
8588     preventMark: false,
8589     isFormField : true,
8590     value : '',
8591     labelWidth : 2,
8592     labelAlign : false,
8593     readOnly : false,
8594     align : false,
8595     formatedValue : false,
8596     forceFeedback : false,
8597     
8598     indicatorpos : 'left',
8599     
8600     labellg : 0,
8601     labelmd : 0,
8602     labelsm : 0,
8603     labelxs : 0,
8604     
8605     parentLabelAlign : function()
8606     {
8607         var parent = this;
8608         while (parent.parent()) {
8609             parent = parent.parent();
8610             if (typeof(parent.labelAlign) !='undefined') {
8611                 return parent.labelAlign;
8612             }
8613         }
8614         return 'left';
8615         
8616     },
8617     
8618     getAutoCreate : function()
8619     {
8620         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8621         
8622         var id = Roo.id();
8623         
8624         var cfg = {};
8625         
8626         if(this.inputType != 'hidden'){
8627             cfg.cls = 'form-group' //input-group
8628         }
8629         
8630         var input =  {
8631             tag: 'input',
8632             id : id,
8633             type : this.inputType,
8634             value : this.value,
8635             cls : 'form-control',
8636             placeholder : this.placeholder || '',
8637             autocomplete : this.autocomplete || 'new-password'
8638         };
8639         
8640         if(this.align){
8641             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8642         }
8643         
8644         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8645             input.maxLength = this.maxLength;
8646         }
8647         
8648         if (this.disabled) {
8649             input.disabled=true;
8650         }
8651         
8652         if (this.readOnly) {
8653             input.readonly=true;
8654         }
8655         
8656         if (this.name) {
8657             input.name = this.name;
8658         }
8659         
8660         if (this.size) {
8661             input.cls += ' input-' + this.size;
8662         }
8663         
8664         var settings=this;
8665         ['xs','sm','md','lg'].map(function(size){
8666             if (settings[size]) {
8667                 cfg.cls += ' col-' + size + '-' + settings[size];
8668             }
8669         });
8670         
8671         var inputblock = input;
8672         
8673         var feedback = {
8674             tag: 'span',
8675             cls: 'glyphicon form-control-feedback'
8676         };
8677             
8678         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8679             
8680             inputblock = {
8681                 cls : 'has-feedback',
8682                 cn :  [
8683                     input,
8684                     feedback
8685                 ] 
8686             };  
8687         }
8688         
8689         if (this.before || this.after) {
8690             
8691             inputblock = {
8692                 cls : 'input-group',
8693                 cn :  [] 
8694             };
8695             
8696             if (this.before && typeof(this.before) == 'string') {
8697                 
8698                 inputblock.cn.push({
8699                     tag :'span',
8700                     cls : 'roo-input-before input-group-addon',
8701                     html : this.before
8702                 });
8703             }
8704             if (this.before && typeof(this.before) == 'object') {
8705                 this.before = Roo.factory(this.before);
8706                 
8707                 inputblock.cn.push({
8708                     tag :'span',
8709                     cls : 'roo-input-before input-group-' +
8710                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8711                 });
8712             }
8713             
8714             inputblock.cn.push(input);
8715             
8716             if (this.after && typeof(this.after) == 'string') {
8717                 inputblock.cn.push({
8718                     tag :'span',
8719                     cls : 'roo-input-after input-group-addon',
8720                     html : this.after
8721                 });
8722             }
8723             if (this.after && typeof(this.after) == 'object') {
8724                 this.after = Roo.factory(this.after);
8725                 
8726                 inputblock.cn.push({
8727                     tag :'span',
8728                     cls : 'roo-input-after input-group-' +
8729                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8730                 });
8731             }
8732             
8733             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8734                 inputblock.cls += ' has-feedback';
8735                 inputblock.cn.push(feedback);
8736             }
8737         };
8738         
8739         if (align ==='left' && this.fieldLabel.length) {
8740             
8741             cfg.cls += ' roo-form-group-label-left';
8742             
8743             cfg.cn = [
8744                 {
8745                     tag : 'i',
8746                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8747                     tooltip : 'This field is required'
8748                 },
8749                 {
8750                     tag: 'label',
8751                     'for' :  id,
8752                     cls : 'control-label',
8753                     html : this.fieldLabel
8754
8755                 },
8756                 {
8757                     cls : "", 
8758                     cn: [
8759                         inputblock
8760                     ]
8761                 }
8762             ];
8763             
8764             var labelCfg = cfg.cn[1];
8765             var contentCfg = cfg.cn[2];
8766             
8767             if(this.indicatorpos == 'right'){
8768                 cfg.cn = [
8769                     {
8770                         tag: 'label',
8771                         'for' :  id,
8772                         cls : 'control-label',
8773                         cn : [
8774                             {
8775                                 tag : 'span',
8776                                 html : this.fieldLabel
8777                             },
8778                             {
8779                                 tag : 'i',
8780                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8781                                 tooltip : 'This field is required'
8782                             }
8783                         ]
8784                     },
8785                     {
8786                         cls : "",
8787                         cn: [
8788                             inputblock
8789                         ]
8790                     }
8791
8792                 ];
8793                 
8794                 labelCfg = cfg.cn[0];
8795                 contentCfg = cfg.cn[1];
8796             
8797             }
8798             
8799             if(this.labelWidth > 12){
8800                 labelCfg.style = "width: " + this.labelWidth + 'px';
8801             }
8802             
8803             if(this.labelWidth < 13 && this.labelmd == 0){
8804                 this.labelmd = this.labelWidth;
8805             }
8806             
8807             if(this.labellg > 0){
8808                 labelCfg.cls += ' col-lg-' + this.labellg;
8809                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8810             }
8811             
8812             if(this.labelmd > 0){
8813                 labelCfg.cls += ' col-md-' + this.labelmd;
8814                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8815             }
8816             
8817             if(this.labelsm > 0){
8818                 labelCfg.cls += ' col-sm-' + this.labelsm;
8819                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8820             }
8821             
8822             if(this.labelxs > 0){
8823                 labelCfg.cls += ' col-xs-' + this.labelxs;
8824                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8825             }
8826             
8827             
8828         } else if ( this.fieldLabel.length) {
8829                 
8830             cfg.cn = [
8831                 {
8832                     tag : 'i',
8833                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8834                     tooltip : 'This field is required'
8835                 },
8836                 {
8837                     tag: 'label',
8838                    //cls : 'input-group-addon',
8839                     html : this.fieldLabel
8840
8841                 },
8842
8843                inputblock
8844
8845            ];
8846            
8847            if(this.indicatorpos == 'right'){
8848                 
8849                 cfg.cn = [
8850                     {
8851                         tag: 'label',
8852                        //cls : 'input-group-addon',
8853                         html : this.fieldLabel
8854
8855                     },
8856                     {
8857                         tag : 'i',
8858                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8859                         tooltip : 'This field is required'
8860                     },
8861
8862                    inputblock
8863
8864                ];
8865
8866             }
8867
8868         } else {
8869             
8870             cfg.cn = [
8871
8872                     inputblock
8873
8874             ];
8875                 
8876                 
8877         };
8878         
8879         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8880            cfg.cls += ' navbar-form';
8881         }
8882         
8883         if (this.parentType === 'NavGroup') {
8884            cfg.cls += ' navbar-form';
8885            cfg.tag = 'li';
8886         }
8887         
8888         return cfg;
8889         
8890     },
8891     /**
8892      * return the real input element.
8893      */
8894     inputEl: function ()
8895     {
8896         return this.el.select('input.form-control',true).first();
8897     },
8898     
8899     tooltipEl : function()
8900     {
8901         return this.inputEl();
8902     },
8903     
8904     indicatorEl : function()
8905     {
8906         var indicator = this.el.select('i.roo-required-indicator',true).first();
8907         
8908         if(!indicator){
8909             return false;
8910         }
8911         
8912         return indicator;
8913         
8914     },
8915     
8916     setDisabled : function(v)
8917     {
8918         var i  = this.inputEl().dom;
8919         if (!v) {
8920             i.removeAttribute('disabled');
8921             return;
8922             
8923         }
8924         i.setAttribute('disabled','true');
8925     },
8926     initEvents : function()
8927     {
8928           
8929         this.inputEl().on("keydown" , this.fireKey,  this);
8930         this.inputEl().on("focus", this.onFocus,  this);
8931         this.inputEl().on("blur", this.onBlur,  this);
8932         
8933         this.inputEl().relayEvent('keyup', this);
8934         
8935         this.indicator = this.indicatorEl();
8936         
8937         if(this.indicator){
8938             this.indicator.addClass('invisible');
8939             
8940         }
8941  
8942         // reference to original value for reset
8943         this.originalValue = this.getValue();
8944         //Roo.form.TextField.superclass.initEvents.call(this);
8945         if(this.validationEvent == 'keyup'){
8946             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8947             this.inputEl().on('keyup', this.filterValidation, this);
8948         }
8949         else if(this.validationEvent !== false){
8950             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8951         }
8952         
8953         if(this.selectOnFocus){
8954             this.on("focus", this.preFocus, this);
8955             
8956         }
8957         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8958             this.inputEl().on("keypress", this.filterKeys, this);
8959         } else {
8960             this.inputEl().relayEvent('keypress', this);
8961         }
8962        /* if(this.grow){
8963             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8964             this.el.on("click", this.autoSize,  this);
8965         }
8966         */
8967         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8968             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8969         }
8970         
8971         if (typeof(this.before) == 'object') {
8972             this.before.render(this.el.select('.roo-input-before',true).first());
8973         }
8974         if (typeof(this.after) == 'object') {
8975             this.after.render(this.el.select('.roo-input-after',true).first());
8976         }
8977         
8978         
8979     },
8980     filterValidation : function(e){
8981         if(!e.isNavKeyPress()){
8982             this.validationTask.delay(this.validationDelay);
8983         }
8984     },
8985      /**
8986      * Validates the field value
8987      * @return {Boolean} True if the value is valid, else false
8988      */
8989     validate : function(){
8990         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8991         if(this.disabled || this.validateValue(this.getRawValue())){
8992             this.markValid();
8993             return true;
8994         }
8995         
8996         this.markInvalid();
8997         return false;
8998     },
8999     
9000     
9001     /**
9002      * Validates a value according to the field's validation rules and marks the field as invalid
9003      * if the validation fails
9004      * @param {Mixed} value The value to validate
9005      * @return {Boolean} True if the value is valid, else false
9006      */
9007     validateValue : function(value){
9008         if(value.length < 1)  { // if it's blank
9009             if(this.allowBlank){
9010                 return true;
9011             }            
9012             return this.inputEl().hasClass('hide') ? true : false;
9013         }
9014         
9015         if(value.length < this.minLength){
9016             return false;
9017         }
9018         if(value.length > this.maxLength){
9019             return false;
9020         }
9021         if(this.vtype){
9022             var vt = Roo.form.VTypes;
9023             if(!vt[this.vtype](value, this)){
9024                 return false;
9025             }
9026         }
9027         if(typeof this.validator == "function"){
9028             var msg = this.validator(value);
9029             if(msg !== true){
9030                 return false;
9031             }
9032             if (typeof(msg) == 'string') {
9033                 this.invalidText = msg;
9034             }
9035         }
9036         
9037         if(this.regex && !this.regex.test(value)){
9038             return false;
9039         }
9040         
9041         return true;
9042     },
9043
9044     
9045     
9046      // private
9047     fireKey : function(e){
9048         //Roo.log('field ' + e.getKey());
9049         if(e.isNavKeyPress()){
9050             this.fireEvent("specialkey", this, e);
9051         }
9052     },
9053     focus : function (selectText){
9054         if(this.rendered){
9055             this.inputEl().focus();
9056             if(selectText === true){
9057                 this.inputEl().dom.select();
9058             }
9059         }
9060         return this;
9061     } ,
9062     
9063     onFocus : function(){
9064         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9065            // this.el.addClass(this.focusClass);
9066         }
9067         if(!this.hasFocus){
9068             this.hasFocus = true;
9069             this.startValue = this.getValue();
9070             this.fireEvent("focus", this);
9071         }
9072     },
9073     
9074     beforeBlur : Roo.emptyFn,
9075
9076     
9077     // private
9078     onBlur : function(){
9079         this.beforeBlur();
9080         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9081             //this.el.removeClass(this.focusClass);
9082         }
9083         this.hasFocus = false;
9084         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9085             this.validate();
9086         }
9087         var v = this.getValue();
9088         if(String(v) !== String(this.startValue)){
9089             this.fireEvent('change', this, v, this.startValue);
9090         }
9091         this.fireEvent("blur", this);
9092     },
9093     
9094     /**
9095      * Resets the current field value to the originally loaded value and clears any validation messages
9096      */
9097     reset : function(){
9098         this.setValue(this.originalValue);
9099         this.validate();
9100     },
9101      /**
9102      * Returns the name of the field
9103      * @return {Mixed} name The name field
9104      */
9105     getName: function(){
9106         return this.name;
9107     },
9108      /**
9109      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9110      * @return {Mixed} value The field value
9111      */
9112     getValue : function(){
9113         
9114         var v = this.inputEl().getValue();
9115         
9116         return v;
9117     },
9118     /**
9119      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9120      * @return {Mixed} value The field value
9121      */
9122     getRawValue : function(){
9123         var v = this.inputEl().getValue();
9124         
9125         return v;
9126     },
9127     
9128     /**
9129      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9130      * @param {Mixed} value The value to set
9131      */
9132     setRawValue : function(v){
9133         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9134     },
9135     
9136     selectText : function(start, end){
9137         var v = this.getRawValue();
9138         if(v.length > 0){
9139             start = start === undefined ? 0 : start;
9140             end = end === undefined ? v.length : end;
9141             var d = this.inputEl().dom;
9142             if(d.setSelectionRange){
9143                 d.setSelectionRange(start, end);
9144             }else if(d.createTextRange){
9145                 var range = d.createTextRange();
9146                 range.moveStart("character", start);
9147                 range.moveEnd("character", v.length-end);
9148                 range.select();
9149             }
9150         }
9151     },
9152     
9153     /**
9154      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9155      * @param {Mixed} value The value to set
9156      */
9157     setValue : function(v){
9158         this.value = v;
9159         if(this.rendered){
9160             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9161             this.validate();
9162         }
9163     },
9164     
9165     /*
9166     processValue : function(value){
9167         if(this.stripCharsRe){
9168             var newValue = value.replace(this.stripCharsRe, '');
9169             if(newValue !== value){
9170                 this.setRawValue(newValue);
9171                 return newValue;
9172             }
9173         }
9174         return value;
9175     },
9176   */
9177     preFocus : function(){
9178         
9179         if(this.selectOnFocus){
9180             this.inputEl().dom.select();
9181         }
9182     },
9183     filterKeys : function(e){
9184         var k = e.getKey();
9185         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9186             return;
9187         }
9188         var c = e.getCharCode(), cc = String.fromCharCode(c);
9189         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9190             return;
9191         }
9192         if(!this.maskRe.test(cc)){
9193             e.stopEvent();
9194         }
9195     },
9196      /**
9197      * Clear any invalid styles/messages for this field
9198      */
9199     clearInvalid : function(){
9200         
9201         if(!this.el || this.preventMark){ // not rendered
9202             return;
9203         }
9204         
9205      
9206         this.el.removeClass(this.invalidClass);
9207         
9208         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9209             
9210             var feedback = this.el.select('.form-control-feedback', true).first();
9211             
9212             if(feedback){
9213                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9214             }
9215             
9216         }
9217         
9218         this.fireEvent('valid', this);
9219     },
9220     
9221      /**
9222      * Mark this field as valid
9223      */
9224     markValid : function()
9225     {
9226         if(!this.el  || this.preventMark){ // not rendered...
9227             return;
9228         }
9229         
9230         this.el.removeClass([this.invalidClass, this.validClass]);
9231         
9232         var feedback = this.el.select('.form-control-feedback', true).first();
9233             
9234         if(feedback){
9235             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9236         }
9237
9238         if(this.disabled){
9239             return;
9240         }
9241         
9242         if(this.allowBlank && !this.getRawValue().length){
9243             return;
9244         }
9245         
9246         if(this.indicator){
9247             this.indicator.removeClass('visible');
9248             this.indicator.addClass('invisible');
9249         }
9250         
9251         this.el.addClass(this.validClass);
9252         
9253         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9254             
9255             var feedback = this.el.select('.form-control-feedback', true).first();
9256             
9257             if(feedback){
9258                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9259                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9260             }
9261             
9262         }
9263         
9264         this.fireEvent('valid', this);
9265     },
9266     
9267      /**
9268      * Mark this field as invalid
9269      * @param {String} msg The validation message
9270      */
9271     markInvalid : function(msg)
9272     {
9273         if(!this.el  || this.preventMark){ // not rendered
9274             return;
9275         }
9276         
9277         this.el.removeClass([this.invalidClass, this.validClass]);
9278         
9279         var feedback = this.el.select('.form-control-feedback', true).first();
9280             
9281         if(feedback){
9282             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9283         }
9284
9285         if(this.disabled){
9286             return;
9287         }
9288         
9289         if(this.allowBlank && !this.getRawValue().length){
9290             return;
9291         }
9292         
9293         if(this.indicator){
9294             this.indicator.removeClass('invisible');
9295             this.indicator.addClass('visible');
9296         }
9297         
9298         this.el.addClass(this.invalidClass);
9299         
9300         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9301             
9302             var feedback = this.el.select('.form-control-feedback', true).first();
9303             
9304             if(feedback){
9305                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9306                 
9307                 if(this.getValue().length || this.forceFeedback){
9308                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9309                 }
9310                 
9311             }
9312             
9313         }
9314         
9315         this.fireEvent('invalid', this, msg);
9316     },
9317     // private
9318     SafariOnKeyDown : function(event)
9319     {
9320         // this is a workaround for a password hang bug on chrome/ webkit.
9321         if (this.inputEl().dom.type != 'password') {
9322             return;
9323         }
9324         
9325         var isSelectAll = false;
9326         
9327         if(this.inputEl().dom.selectionEnd > 0){
9328             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9329         }
9330         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9331             event.preventDefault();
9332             this.setValue('');
9333             return;
9334         }
9335         
9336         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9337             
9338             event.preventDefault();
9339             // this is very hacky as keydown always get's upper case.
9340             //
9341             var cc = String.fromCharCode(event.getCharCode());
9342             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9343             
9344         }
9345     },
9346     adjustWidth : function(tag, w){
9347         tag = tag.toLowerCase();
9348         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9349             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9350                 if(tag == 'input'){
9351                     return w + 2;
9352                 }
9353                 if(tag == 'textarea'){
9354                     return w-2;
9355                 }
9356             }else if(Roo.isOpera){
9357                 if(tag == 'input'){
9358                     return w + 2;
9359                 }
9360                 if(tag == 'textarea'){
9361                     return w-2;
9362                 }
9363             }
9364         }
9365         return w;
9366     },
9367     
9368     setFieldLabel : function(v)
9369     {
9370         if(!this.rendered){
9371             return;
9372         }
9373         
9374         this.fieldLabel = v;
9375         
9376         if(this.indicator){
9377             var ar = this.el.select('label > span',true);
9378             if (!ar.elements.length) {
9379                 Roo.log("could not find label > span on element");
9380                 Roo.log(this);
9381                 return;
9382             }
9383             this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9384             return;
9385         }
9386         
9387         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9388     }
9389 });
9390
9391  
9392 /*
9393  * - LGPL
9394  *
9395  * Input
9396  * 
9397  */
9398
9399 /**
9400  * @class Roo.bootstrap.TextArea
9401  * @extends Roo.bootstrap.Input
9402  * Bootstrap TextArea class
9403  * @cfg {Number} cols Specifies the visible width of a text area
9404  * @cfg {Number} rows Specifies the visible number of lines in a text area
9405  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9406  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9407  * @cfg {string} html text
9408  * 
9409  * @constructor
9410  * Create a new TextArea
9411  * @param {Object} config The config object
9412  */
9413
9414 Roo.bootstrap.TextArea = function(config){
9415     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9416    
9417 };
9418
9419 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9420      
9421     cols : false,
9422     rows : 5,
9423     readOnly : false,
9424     warp : 'soft',
9425     resize : false,
9426     value: false,
9427     html: false,
9428     
9429     getAutoCreate : function(){
9430         
9431         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9432         
9433         var id = Roo.id();
9434         
9435         var cfg = {};
9436         
9437         if(this.inputType != 'hidden'){
9438             cfg.cls = 'form-group' //input-group
9439         }
9440         
9441         var input =  {
9442             tag: 'textarea',
9443             id : id,
9444             warp : this.warp,
9445             rows : this.rows,
9446             value : this.value || '',
9447             html: this.html || '',
9448             cls : 'form-control',
9449             placeholder : this.placeholder || '' 
9450             
9451         };
9452         
9453         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9454             input.maxLength = this.maxLength;
9455         }
9456         
9457         if(this.resize){
9458             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9459         }
9460         
9461         if(this.cols){
9462             input.cols = this.cols;
9463         }
9464         
9465         if (this.readOnly) {
9466             input.readonly = true;
9467         }
9468         
9469         if (this.name) {
9470             input.name = this.name;
9471         }
9472         
9473         if (this.size) {
9474             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9475         }
9476         
9477         var settings=this;
9478         ['xs','sm','md','lg'].map(function(size){
9479             if (settings[size]) {
9480                 cfg.cls += ' col-' + size + '-' + settings[size];
9481             }
9482         });
9483         
9484         var inputblock = input;
9485         
9486         if(this.hasFeedback && !this.allowBlank){
9487             
9488             var feedback = {
9489                 tag: 'span',
9490                 cls: 'glyphicon form-control-feedback'
9491             };
9492
9493             inputblock = {
9494                 cls : 'has-feedback',
9495                 cn :  [
9496                     input,
9497                     feedback
9498                 ] 
9499             };  
9500         }
9501         
9502         
9503         if (this.before || this.after) {
9504             
9505             inputblock = {
9506                 cls : 'input-group',
9507                 cn :  [] 
9508             };
9509             if (this.before) {
9510                 inputblock.cn.push({
9511                     tag :'span',
9512                     cls : 'input-group-addon',
9513                     html : this.before
9514                 });
9515             }
9516             
9517             inputblock.cn.push(input);
9518             
9519             if(this.hasFeedback && !this.allowBlank){
9520                 inputblock.cls += ' has-feedback';
9521                 inputblock.cn.push(feedback);
9522             }
9523             
9524             if (this.after) {
9525                 inputblock.cn.push({
9526                     tag :'span',
9527                     cls : 'input-group-addon',
9528                     html : this.after
9529                 });
9530             }
9531             
9532         }
9533         
9534         if (align ==='left' && this.fieldLabel.length) {
9535             cfg.cn = [
9536                 {
9537                     tag: 'label',
9538                     'for' :  id,
9539                     cls : 'control-label',
9540                     html : this.fieldLabel
9541                 },
9542                 {
9543                     cls : "",
9544                     cn: [
9545                         inputblock
9546                     ]
9547                 }
9548
9549             ];
9550             
9551             if(this.labelWidth > 12){
9552                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9553             }
9554
9555             if(this.labelWidth < 13 && this.labelmd == 0){
9556                 this.labelmd = this.labelWidth;
9557             }
9558
9559             if(this.labellg > 0){
9560                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9561                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9562             }
9563
9564             if(this.labelmd > 0){
9565                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9566                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9567             }
9568
9569             if(this.labelsm > 0){
9570                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9571                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9572             }
9573
9574             if(this.labelxs > 0){
9575                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9576                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9577             }
9578             
9579         } else if ( this.fieldLabel.length) {
9580             cfg.cn = [
9581
9582                {
9583                    tag: 'label',
9584                    //cls : 'input-group-addon',
9585                    html : this.fieldLabel
9586
9587                },
9588
9589                inputblock
9590
9591            ];
9592
9593         } else {
9594
9595             cfg.cn = [
9596
9597                 inputblock
9598
9599             ];
9600                 
9601         }
9602         
9603         if (this.disabled) {
9604             input.disabled=true;
9605         }
9606         
9607         return cfg;
9608         
9609     },
9610     /**
9611      * return the real textarea element.
9612      */
9613     inputEl: function ()
9614     {
9615         return this.el.select('textarea.form-control',true).first();
9616     },
9617     
9618     /**
9619      * Clear any invalid styles/messages for this field
9620      */
9621     clearInvalid : function()
9622     {
9623         
9624         if(!this.el || this.preventMark){ // not rendered
9625             return;
9626         }
9627         
9628         var label = this.el.select('label', true).first();
9629         var icon = this.el.select('i.fa-star', true).first();
9630         
9631         if(label && icon){
9632             icon.remove();
9633         }
9634         
9635         this.el.removeClass(this.invalidClass);
9636         
9637         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
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);
9643             }
9644             
9645         }
9646         
9647         this.fireEvent('valid', this);
9648     },
9649     
9650      /**
9651      * Mark this field as valid
9652      */
9653     markValid : function()
9654     {
9655         if(!this.el  || this.preventMark){ // not rendered
9656             return;
9657         }
9658         
9659         this.el.removeClass([this.invalidClass, this.validClass]);
9660         
9661         var feedback = this.el.select('.form-control-feedback', true).first();
9662             
9663         if(feedback){
9664             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9665         }
9666
9667         if(this.disabled || this.allowBlank){
9668             return;
9669         }
9670         
9671         var label = this.el.select('label', true).first();
9672         var icon = this.el.select('i.fa-star', true).first();
9673         
9674         if(label && icon){
9675             icon.remove();
9676         }
9677         
9678         this.el.addClass(this.validClass);
9679         
9680         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9681             
9682             var feedback = this.el.select('.form-control-feedback', true).first();
9683             
9684             if(feedback){
9685                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9686                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9687             }
9688             
9689         }
9690         
9691         this.fireEvent('valid', this);
9692     },
9693     
9694      /**
9695      * Mark this field as invalid
9696      * @param {String} msg The validation message
9697      */
9698     markInvalid : function(msg)
9699     {
9700         if(!this.el  || this.preventMark){ // not rendered
9701             return;
9702         }
9703         
9704         this.el.removeClass([this.invalidClass, this.validClass]);
9705         
9706         var feedback = this.el.select('.form-control-feedback', true).first();
9707             
9708         if(feedback){
9709             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9710         }
9711
9712         if(this.disabled || this.allowBlank){
9713             return;
9714         }
9715         
9716         var label = this.el.select('label', true).first();
9717         var icon = this.el.select('i.fa-star', true).first();
9718         
9719         if(!this.getValue().length && label && !icon){
9720             this.el.createChild({
9721                 tag : 'i',
9722                 cls : 'text-danger fa fa-lg fa-star',
9723                 tooltip : 'This field is required',
9724                 style : 'margin-right:5px;'
9725             }, label, true);
9726         }
9727
9728         this.el.addClass(this.invalidClass);
9729         
9730         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9731             
9732             var feedback = this.el.select('.form-control-feedback', true).first();
9733             
9734             if(feedback){
9735                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9736                 
9737                 if(this.getValue().length || this.forceFeedback){
9738                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9739                 }
9740                 
9741             }
9742             
9743         }
9744         
9745         this.fireEvent('invalid', this, msg);
9746     }
9747 });
9748
9749  
9750 /*
9751  * - LGPL
9752  *
9753  * trigger field - base class for combo..
9754  * 
9755  */
9756  
9757 /**
9758  * @class Roo.bootstrap.TriggerField
9759  * @extends Roo.bootstrap.Input
9760  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9761  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9762  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9763  * for which you can provide a custom implementation.  For example:
9764  * <pre><code>
9765 var trigger = new Roo.bootstrap.TriggerField();
9766 trigger.onTriggerClick = myTriggerFn;
9767 trigger.applyTo('my-field');
9768 </code></pre>
9769  *
9770  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9771  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9772  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9773  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9774  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9775
9776  * @constructor
9777  * Create a new TriggerField.
9778  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9779  * to the base TextField)
9780  */
9781 Roo.bootstrap.TriggerField = function(config){
9782     this.mimicing = false;
9783     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9784 };
9785
9786 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9787     /**
9788      * @cfg {String} triggerClass A CSS class to apply to the trigger
9789      */
9790      /**
9791      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9792      */
9793     hideTrigger:false,
9794
9795     /**
9796      * @cfg {Boolean} removable (true|false) special filter default false
9797      */
9798     removable : false,
9799     
9800     /** @cfg {Boolean} grow @hide */
9801     /** @cfg {Number} growMin @hide */
9802     /** @cfg {Number} growMax @hide */
9803
9804     /**
9805      * @hide 
9806      * @method
9807      */
9808     autoSize: Roo.emptyFn,
9809     // private
9810     monitorTab : true,
9811     // private
9812     deferHeight : true,
9813
9814     
9815     actionMode : 'wrap',
9816     
9817     caret : false,
9818     
9819     
9820     getAutoCreate : function(){
9821        
9822         var align = this.labelAlign || this.parentLabelAlign();
9823         
9824         var id = Roo.id();
9825         
9826         var cfg = {
9827             cls: 'form-group' //input-group
9828         };
9829         
9830         
9831         var input =  {
9832             tag: 'input',
9833             id : id,
9834             type : this.inputType,
9835             cls : 'form-control',
9836             autocomplete: 'new-password',
9837             placeholder : this.placeholder || '' 
9838             
9839         };
9840         if (this.name) {
9841             input.name = this.name;
9842         }
9843         if (this.size) {
9844             input.cls += ' input-' + this.size;
9845         }
9846         
9847         if (this.disabled) {
9848             input.disabled=true;
9849         }
9850         
9851         var inputblock = input;
9852         
9853         if(this.hasFeedback && !this.allowBlank){
9854             
9855             var feedback = {
9856                 tag: 'span',
9857                 cls: 'glyphicon form-control-feedback'
9858             };
9859             
9860             if(this.removable && !this.editable && !this.tickable){
9861                 inputblock = {
9862                     cls : 'has-feedback',
9863                     cn :  [
9864                         inputblock,
9865                         {
9866                             tag: 'button',
9867                             html : 'x',
9868                             cls : 'roo-combo-removable-btn close'
9869                         },
9870                         feedback
9871                     ] 
9872                 };
9873             } else {
9874                 inputblock = {
9875                     cls : 'has-feedback',
9876                     cn :  [
9877                         inputblock,
9878                         feedback
9879                     ] 
9880                 };
9881             }
9882
9883         } else {
9884             if(this.removable && !this.editable && !this.tickable){
9885                 inputblock = {
9886                     cls : 'roo-removable',
9887                     cn :  [
9888                         inputblock,
9889                         {
9890                             tag: 'button',
9891                             html : 'x',
9892                             cls : 'roo-combo-removable-btn close'
9893                         }
9894                     ] 
9895                 };
9896             }
9897         }
9898         
9899         if (this.before || this.after) {
9900             
9901             inputblock = {
9902                 cls : 'input-group',
9903                 cn :  [] 
9904             };
9905             if (this.before) {
9906                 inputblock.cn.push({
9907                     tag :'span',
9908                     cls : 'input-group-addon',
9909                     html : this.before
9910                 });
9911             }
9912             
9913             inputblock.cn.push(input);
9914             
9915             if(this.hasFeedback && !this.allowBlank){
9916                 inputblock.cls += ' has-feedback';
9917                 inputblock.cn.push(feedback);
9918             }
9919             
9920             if (this.after) {
9921                 inputblock.cn.push({
9922                     tag :'span',
9923                     cls : 'input-group-addon',
9924                     html : this.after
9925                 });
9926             }
9927             
9928         };
9929         
9930         var box = {
9931             tag: 'div',
9932             cn: [
9933                 {
9934                     tag: 'input',
9935                     type : 'hidden',
9936                     cls: 'form-hidden-field'
9937                 },
9938                 inputblock
9939             ]
9940             
9941         };
9942         
9943         if(this.multiple){
9944             box = {
9945                 tag: 'div',
9946                 cn: [
9947                     {
9948                         tag: 'input',
9949                         type : 'hidden',
9950                         cls: 'form-hidden-field'
9951                     },
9952                     {
9953                         tag: 'ul',
9954                         cls: 'roo-select2-choices',
9955                         cn:[
9956                             {
9957                                 tag: 'li',
9958                                 cls: 'roo-select2-search-field',
9959                                 cn: [
9960
9961                                     inputblock
9962                                 ]
9963                             }
9964                         ]
9965                     }
9966                 ]
9967             }
9968         };
9969         
9970         var combobox = {
9971             cls: 'roo-select2-container input-group',
9972             cn: [
9973                 box
9974 //                {
9975 //                    tag: 'ul',
9976 //                    cls: 'typeahead typeahead-long dropdown-menu',
9977 //                    style: 'display:none'
9978 //                }
9979             ]
9980         };
9981         
9982         if(!this.multiple && this.showToggleBtn){
9983             
9984             var caret = {
9985                         tag: 'span',
9986                         cls: 'caret'
9987              };
9988             if (this.caret != false) {
9989                 caret = {
9990                      tag: 'i',
9991                      cls: 'fa fa-' + this.caret
9992                 };
9993                 
9994             }
9995             
9996             combobox.cn.push({
9997                 tag :'span',
9998                 cls : 'input-group-addon btn dropdown-toggle',
9999                 cn : [
10000                     caret,
10001                     {
10002                         tag: 'span',
10003                         cls: 'combobox-clear',
10004                         cn  : [
10005                             {
10006                                 tag : 'i',
10007                                 cls: 'icon-remove'
10008                             }
10009                         ]
10010                     }
10011                 ]
10012
10013             })
10014         }
10015         
10016         if(this.multiple){
10017             combobox.cls += ' roo-select2-container-multi';
10018         }
10019         
10020         if (align ==='left' && this.fieldLabel.length) {
10021             
10022             cfg.cls += ' roo-form-group-label-left';
10023
10024             cfg.cn = [
10025                 {
10026                     tag : 'i',
10027                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10028                     tooltip : 'This field is required'
10029                 },
10030                 {
10031                     tag: 'label',
10032                     'for' :  id,
10033                     cls : 'control-label',
10034                     html : this.fieldLabel
10035
10036                 },
10037                 {
10038                     cls : "", 
10039                     cn: [
10040                         combobox
10041                     ]
10042                 }
10043
10044             ];
10045             
10046             var labelCfg = cfg.cn[1];
10047             var contentCfg = cfg.cn[2];
10048             
10049             if(this.indicatorpos == 'right'){
10050                 cfg.cn = [
10051                     {
10052                         tag: 'label',
10053                         'for' :  id,
10054                         cls : 'control-label',
10055                         cn : [
10056                             {
10057                                 tag : 'span',
10058                                 html : this.fieldLabel
10059                             },
10060                             {
10061                                 tag : 'i',
10062                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10063                                 tooltip : 'This field is required'
10064                             }
10065                         ]
10066                     },
10067                     {
10068                         cls : "", 
10069                         cn: [
10070                             combobox
10071                         ]
10072                     }
10073
10074                 ];
10075                 
10076                 labelCfg = cfg.cn[0];
10077                 contentCfg = cfg.cn[1];
10078             }
10079             
10080             if(this.labelWidth > 12){
10081                 labelCfg.style = "width: " + this.labelWidth + 'px';
10082             }
10083             
10084             if(this.labelWidth < 13 && this.labelmd == 0){
10085                 this.labelmd = this.labelWidth;
10086             }
10087             
10088             if(this.labellg > 0){
10089                 labelCfg.cls += ' col-lg-' + this.labellg;
10090                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10091             }
10092             
10093             if(this.labelmd > 0){
10094                 labelCfg.cls += ' col-md-' + this.labelmd;
10095                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10096             }
10097             
10098             if(this.labelsm > 0){
10099                 labelCfg.cls += ' col-sm-' + this.labelsm;
10100                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10101             }
10102             
10103             if(this.labelxs > 0){
10104                 labelCfg.cls += ' col-xs-' + this.labelxs;
10105                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10106             }
10107             
10108         } else if ( this.fieldLabel.length) {
10109 //                Roo.log(" label");
10110             cfg.cn = [
10111                 {
10112                    tag : 'i',
10113                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10114                    tooltip : 'This field is required'
10115                },
10116                {
10117                    tag: 'label',
10118                    //cls : 'input-group-addon',
10119                    html : this.fieldLabel
10120
10121                },
10122
10123                combobox
10124
10125             ];
10126             
10127             if(this.indicatorpos == 'right'){
10128                 
10129                 cfg.cn = [
10130                     {
10131                        tag: 'label',
10132                        cn : [
10133                            {
10134                                tag : 'span',
10135                                html : this.fieldLabel
10136                            },
10137                            {
10138                               tag : 'i',
10139                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10140                               tooltip : 'This field is required'
10141                            }
10142                        ]
10143
10144                     },
10145                     combobox
10146
10147                 ];
10148
10149             }
10150
10151         } else {
10152             
10153 //                Roo.log(" no label && no align");
10154                 cfg = combobox
10155                      
10156                 
10157         }
10158         
10159         var settings=this;
10160         ['xs','sm','md','lg'].map(function(size){
10161             if (settings[size]) {
10162                 cfg.cls += ' col-' + size + '-' + settings[size];
10163             }
10164         });
10165         
10166         return cfg;
10167         
10168     },
10169     
10170     
10171     
10172     // private
10173     onResize : function(w, h){
10174 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10175 //        if(typeof w == 'number'){
10176 //            var x = w - this.trigger.getWidth();
10177 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10178 //            this.trigger.setStyle('left', x+'px');
10179 //        }
10180     },
10181
10182     // private
10183     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10184
10185     // private
10186     getResizeEl : function(){
10187         return this.inputEl();
10188     },
10189
10190     // private
10191     getPositionEl : function(){
10192         return this.inputEl();
10193     },
10194
10195     // private
10196     alignErrorIcon : function(){
10197         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10198     },
10199
10200     // private
10201     initEvents : function(){
10202         
10203         this.createList();
10204         
10205         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10206         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10207         if(!this.multiple && this.showToggleBtn){
10208             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10209             if(this.hideTrigger){
10210                 this.trigger.setDisplayed(false);
10211             }
10212             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10213         }
10214         
10215         if(this.multiple){
10216             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10217         }
10218         
10219         if(this.removable && !this.editable && !this.tickable){
10220             var close = this.closeTriggerEl();
10221             
10222             if(close){
10223                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10224                 close.on('click', this.removeBtnClick, this, close);
10225             }
10226         }
10227         
10228         //this.trigger.addClassOnOver('x-form-trigger-over');
10229         //this.trigger.addClassOnClick('x-form-trigger-click');
10230         
10231         //if(!this.width){
10232         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10233         //}
10234     },
10235     
10236     closeTriggerEl : function()
10237     {
10238         var close = this.el.select('.roo-combo-removable-btn', true).first();
10239         return close ? close : false;
10240     },
10241     
10242     removeBtnClick : function(e, h, el)
10243     {
10244         e.preventDefault();
10245         
10246         if(this.fireEvent("remove", this) !== false){
10247             this.reset();
10248             this.fireEvent("afterremove", this)
10249         }
10250     },
10251     
10252     createList : function()
10253     {
10254         this.list = Roo.get(document.body).createChild({
10255             tag: 'ul',
10256             cls: 'typeahead typeahead-long dropdown-menu',
10257             style: 'display:none'
10258         });
10259         
10260         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10261         
10262     },
10263
10264     // private
10265     initTrigger : function(){
10266        
10267     },
10268
10269     // private
10270     onDestroy : function(){
10271         if(this.trigger){
10272             this.trigger.removeAllListeners();
10273           //  this.trigger.remove();
10274         }
10275         //if(this.wrap){
10276         //    this.wrap.remove();
10277         //}
10278         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10279     },
10280
10281     // private
10282     onFocus : function(){
10283         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10284         /*
10285         if(!this.mimicing){
10286             this.wrap.addClass('x-trigger-wrap-focus');
10287             this.mimicing = true;
10288             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10289             if(this.monitorTab){
10290                 this.el.on("keydown", this.checkTab, this);
10291             }
10292         }
10293         */
10294     },
10295
10296     // private
10297     checkTab : function(e){
10298         if(e.getKey() == e.TAB){
10299             this.triggerBlur();
10300         }
10301     },
10302
10303     // private
10304     onBlur : function(){
10305         // do nothing
10306     },
10307
10308     // private
10309     mimicBlur : function(e, t){
10310         /*
10311         if(!this.wrap.contains(t) && this.validateBlur()){
10312             this.triggerBlur();
10313         }
10314         */
10315     },
10316
10317     // private
10318     triggerBlur : function(){
10319         this.mimicing = false;
10320         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10321         if(this.monitorTab){
10322             this.el.un("keydown", this.checkTab, this);
10323         }
10324         //this.wrap.removeClass('x-trigger-wrap-focus');
10325         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10326     },
10327
10328     // private
10329     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10330     validateBlur : function(e, t){
10331         return true;
10332     },
10333
10334     // private
10335     onDisable : function(){
10336         this.inputEl().dom.disabled = true;
10337         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10338         //if(this.wrap){
10339         //    this.wrap.addClass('x-item-disabled');
10340         //}
10341     },
10342
10343     // private
10344     onEnable : function(){
10345         this.inputEl().dom.disabled = false;
10346         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10347         //if(this.wrap){
10348         //    this.el.removeClass('x-item-disabled');
10349         //}
10350     },
10351
10352     // private
10353     onShow : function(){
10354         var ae = this.getActionEl();
10355         
10356         if(ae){
10357             ae.dom.style.display = '';
10358             ae.dom.style.visibility = 'visible';
10359         }
10360     },
10361
10362     // private
10363     
10364     onHide : function(){
10365         var ae = this.getActionEl();
10366         ae.dom.style.display = 'none';
10367     },
10368
10369     /**
10370      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10371      * by an implementing function.
10372      * @method
10373      * @param {EventObject} e
10374      */
10375     onTriggerClick : Roo.emptyFn
10376 });
10377  /*
10378  * Based on:
10379  * Ext JS Library 1.1.1
10380  * Copyright(c) 2006-2007, Ext JS, LLC.
10381  *
10382  * Originally Released Under LGPL - original licence link has changed is not relivant.
10383  *
10384  * Fork - LGPL
10385  * <script type="text/javascript">
10386  */
10387
10388
10389 /**
10390  * @class Roo.data.SortTypes
10391  * @singleton
10392  * Defines the default sorting (casting?) comparison functions used when sorting data.
10393  */
10394 Roo.data.SortTypes = {
10395     /**
10396      * Default sort that does nothing
10397      * @param {Mixed} s The value being converted
10398      * @return {Mixed} The comparison value
10399      */
10400     none : function(s){
10401         return s;
10402     },
10403     
10404     /**
10405      * The regular expression used to strip tags
10406      * @type {RegExp}
10407      * @property
10408      */
10409     stripTagsRE : /<\/?[^>]+>/gi,
10410     
10411     /**
10412      * Strips all HTML tags to sort on text only
10413      * @param {Mixed} s The value being converted
10414      * @return {String} The comparison value
10415      */
10416     asText : function(s){
10417         return String(s).replace(this.stripTagsRE, "");
10418     },
10419     
10420     /**
10421      * Strips all HTML tags to sort on text only - Case insensitive
10422      * @param {Mixed} s The value being converted
10423      * @return {String} The comparison value
10424      */
10425     asUCText : function(s){
10426         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10427     },
10428     
10429     /**
10430      * Case insensitive string
10431      * @param {Mixed} s The value being converted
10432      * @return {String} The comparison value
10433      */
10434     asUCString : function(s) {
10435         return String(s).toUpperCase();
10436     },
10437     
10438     /**
10439      * Date sorting
10440      * @param {Mixed} s The value being converted
10441      * @return {Number} The comparison value
10442      */
10443     asDate : function(s) {
10444         if(!s){
10445             return 0;
10446         }
10447         if(s instanceof Date){
10448             return s.getTime();
10449         }
10450         return Date.parse(String(s));
10451     },
10452     
10453     /**
10454      * Float sorting
10455      * @param {Mixed} s The value being converted
10456      * @return {Float} The comparison value
10457      */
10458     asFloat : function(s) {
10459         var val = parseFloat(String(s).replace(/,/g, ""));
10460         if(isNaN(val)) {
10461             val = 0;
10462         }
10463         return val;
10464     },
10465     
10466     /**
10467      * Integer sorting
10468      * @param {Mixed} s The value being converted
10469      * @return {Number} The comparison value
10470      */
10471     asInt : function(s) {
10472         var val = parseInt(String(s).replace(/,/g, ""));
10473         if(isNaN(val)) {
10474             val = 0;
10475         }
10476         return val;
10477     }
10478 };/*
10479  * Based on:
10480  * Ext JS Library 1.1.1
10481  * Copyright(c) 2006-2007, Ext JS, LLC.
10482  *
10483  * Originally Released Under LGPL - original licence link has changed is not relivant.
10484  *
10485  * Fork - LGPL
10486  * <script type="text/javascript">
10487  */
10488
10489 /**
10490 * @class Roo.data.Record
10491  * Instances of this class encapsulate both record <em>definition</em> information, and record
10492  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10493  * to access Records cached in an {@link Roo.data.Store} object.<br>
10494  * <p>
10495  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10496  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10497  * objects.<br>
10498  * <p>
10499  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10500  * @constructor
10501  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10502  * {@link #create}. The parameters are the same.
10503  * @param {Array} data An associative Array of data values keyed by the field name.
10504  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10505  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10506  * not specified an integer id is generated.
10507  */
10508 Roo.data.Record = function(data, id){
10509     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10510     this.data = data;
10511 };
10512
10513 /**
10514  * Generate a constructor for a specific record layout.
10515  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10516  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10517  * Each field definition object may contain the following properties: <ul>
10518  * <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,
10519  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10520  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10521  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10522  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10523  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10524  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10525  * this may be omitted.</p></li>
10526  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10527  * <ul><li>auto (Default, implies no conversion)</li>
10528  * <li>string</li>
10529  * <li>int</li>
10530  * <li>float</li>
10531  * <li>boolean</li>
10532  * <li>date</li></ul></p></li>
10533  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10534  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10535  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10536  * by the Reader into an object that will be stored in the Record. It is passed the
10537  * following parameters:<ul>
10538  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10539  * </ul></p></li>
10540  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10541  * </ul>
10542  * <br>usage:<br><pre><code>
10543 var TopicRecord = Roo.data.Record.create(
10544     {name: 'title', mapping: 'topic_title'},
10545     {name: 'author', mapping: 'username'},
10546     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10547     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10548     {name: 'lastPoster', mapping: 'user2'},
10549     {name: 'excerpt', mapping: 'post_text'}
10550 );
10551
10552 var myNewRecord = new TopicRecord({
10553     title: 'Do my job please',
10554     author: 'noobie',
10555     totalPosts: 1,
10556     lastPost: new Date(),
10557     lastPoster: 'Animal',
10558     excerpt: 'No way dude!'
10559 });
10560 myStore.add(myNewRecord);
10561 </code></pre>
10562  * @method create
10563  * @static
10564  */
10565 Roo.data.Record.create = function(o){
10566     var f = function(){
10567         f.superclass.constructor.apply(this, arguments);
10568     };
10569     Roo.extend(f, Roo.data.Record);
10570     var p = f.prototype;
10571     p.fields = new Roo.util.MixedCollection(false, function(field){
10572         return field.name;
10573     });
10574     for(var i = 0, len = o.length; i < len; i++){
10575         p.fields.add(new Roo.data.Field(o[i]));
10576     }
10577     f.getField = function(name){
10578         return p.fields.get(name);  
10579     };
10580     return f;
10581 };
10582
10583 Roo.data.Record.AUTO_ID = 1000;
10584 Roo.data.Record.EDIT = 'edit';
10585 Roo.data.Record.REJECT = 'reject';
10586 Roo.data.Record.COMMIT = 'commit';
10587
10588 Roo.data.Record.prototype = {
10589     /**
10590      * Readonly flag - true if this record has been modified.
10591      * @type Boolean
10592      */
10593     dirty : false,
10594     editing : false,
10595     error: null,
10596     modified: null,
10597
10598     // private
10599     join : function(store){
10600         this.store = store;
10601     },
10602
10603     /**
10604      * Set the named field to the specified value.
10605      * @param {String} name The name of the field to set.
10606      * @param {Object} value The value to set the field to.
10607      */
10608     set : function(name, value){
10609         if(this.data[name] == value){
10610             return;
10611         }
10612         this.dirty = true;
10613         if(!this.modified){
10614             this.modified = {};
10615         }
10616         if(typeof this.modified[name] == 'undefined'){
10617             this.modified[name] = this.data[name];
10618         }
10619         this.data[name] = value;
10620         if(!this.editing && this.store){
10621             this.store.afterEdit(this);
10622         }       
10623     },
10624
10625     /**
10626      * Get the value of the named field.
10627      * @param {String} name The name of the field to get the value of.
10628      * @return {Object} The value of the field.
10629      */
10630     get : function(name){
10631         return this.data[name]; 
10632     },
10633
10634     // private
10635     beginEdit : function(){
10636         this.editing = true;
10637         this.modified = {}; 
10638     },
10639
10640     // private
10641     cancelEdit : function(){
10642         this.editing = false;
10643         delete this.modified;
10644     },
10645
10646     // private
10647     endEdit : function(){
10648         this.editing = false;
10649         if(this.dirty && this.store){
10650             this.store.afterEdit(this);
10651         }
10652     },
10653
10654     /**
10655      * Usually called by the {@link Roo.data.Store} which owns the Record.
10656      * Rejects all changes made to the Record since either creation, or the last commit operation.
10657      * Modified fields are reverted to their original values.
10658      * <p>
10659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10660      * of reject operations.
10661      */
10662     reject : function(){
10663         var m = this.modified;
10664         for(var n in m){
10665             if(typeof m[n] != "function"){
10666                 this.data[n] = m[n];
10667             }
10668         }
10669         this.dirty = false;
10670         delete this.modified;
10671         this.editing = false;
10672         if(this.store){
10673             this.store.afterReject(this);
10674         }
10675     },
10676
10677     /**
10678      * Usually called by the {@link Roo.data.Store} which owns the Record.
10679      * Commits all changes made to the Record since either creation, or the last commit operation.
10680      * <p>
10681      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10682      * of commit operations.
10683      */
10684     commit : function(){
10685         this.dirty = false;
10686         delete this.modified;
10687         this.editing = false;
10688         if(this.store){
10689             this.store.afterCommit(this);
10690         }
10691     },
10692
10693     // private
10694     hasError : function(){
10695         return this.error != null;
10696     },
10697
10698     // private
10699     clearError : function(){
10700         this.error = null;
10701     },
10702
10703     /**
10704      * Creates a copy of this record.
10705      * @param {String} id (optional) A new record id if you don't want to use this record's id
10706      * @return {Record}
10707      */
10708     copy : function(newId) {
10709         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10710     }
10711 };/*
10712  * Based on:
10713  * Ext JS Library 1.1.1
10714  * Copyright(c) 2006-2007, Ext JS, LLC.
10715  *
10716  * Originally Released Under LGPL - original licence link has changed is not relivant.
10717  *
10718  * Fork - LGPL
10719  * <script type="text/javascript">
10720  */
10721
10722
10723
10724 /**
10725  * @class Roo.data.Store
10726  * @extends Roo.util.Observable
10727  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10728  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10729  * <p>
10730  * 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
10731  * has no knowledge of the format of the data returned by the Proxy.<br>
10732  * <p>
10733  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10734  * instances from the data object. These records are cached and made available through accessor functions.
10735  * @constructor
10736  * Creates a new Store.
10737  * @param {Object} config A config object containing the objects needed for the Store to access data,
10738  * and read the data into Records.
10739  */
10740 Roo.data.Store = function(config){
10741     this.data = new Roo.util.MixedCollection(false);
10742     this.data.getKey = function(o){
10743         return o.id;
10744     };
10745     this.baseParams = {};
10746     // private
10747     this.paramNames = {
10748         "start" : "start",
10749         "limit" : "limit",
10750         "sort" : "sort",
10751         "dir" : "dir",
10752         "multisort" : "_multisort"
10753     };
10754
10755     if(config && config.data){
10756         this.inlineData = config.data;
10757         delete config.data;
10758     }
10759
10760     Roo.apply(this, config);
10761     
10762     if(this.reader){ // reader passed
10763         this.reader = Roo.factory(this.reader, Roo.data);
10764         this.reader.xmodule = this.xmodule || false;
10765         if(!this.recordType){
10766             this.recordType = this.reader.recordType;
10767         }
10768         if(this.reader.onMetaChange){
10769             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10770         }
10771     }
10772
10773     if(this.recordType){
10774         this.fields = this.recordType.prototype.fields;
10775     }
10776     this.modified = [];
10777
10778     this.addEvents({
10779         /**
10780          * @event datachanged
10781          * Fires when the data cache has changed, and a widget which is using this Store
10782          * as a Record cache should refresh its view.
10783          * @param {Store} this
10784          */
10785         datachanged : true,
10786         /**
10787          * @event metachange
10788          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10789          * @param {Store} this
10790          * @param {Object} meta The JSON metadata
10791          */
10792         metachange : true,
10793         /**
10794          * @event add
10795          * Fires when Records have been added to the Store
10796          * @param {Store} this
10797          * @param {Roo.data.Record[]} records The array of Records added
10798          * @param {Number} index The index at which the record(s) were added
10799          */
10800         add : true,
10801         /**
10802          * @event remove
10803          * Fires when a Record has been removed from the Store
10804          * @param {Store} this
10805          * @param {Roo.data.Record} record The Record that was removed
10806          * @param {Number} index The index at which the record was removed
10807          */
10808         remove : true,
10809         /**
10810          * @event update
10811          * Fires when a Record has been updated
10812          * @param {Store} this
10813          * @param {Roo.data.Record} record The Record that was updated
10814          * @param {String} operation The update operation being performed.  Value may be one of:
10815          * <pre><code>
10816  Roo.data.Record.EDIT
10817  Roo.data.Record.REJECT
10818  Roo.data.Record.COMMIT
10819          * </code></pre>
10820          */
10821         update : true,
10822         /**
10823          * @event clear
10824          * Fires when the data cache has been cleared.
10825          * @param {Store} this
10826          */
10827         clear : true,
10828         /**
10829          * @event beforeload
10830          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10831          * the load action will be canceled.
10832          * @param {Store} this
10833          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10834          */
10835         beforeload : true,
10836         /**
10837          * @event beforeloadadd
10838          * Fires after a new set of Records has been loaded.
10839          * @param {Store} this
10840          * @param {Roo.data.Record[]} records The Records that were loaded
10841          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10842          */
10843         beforeloadadd : true,
10844         /**
10845          * @event load
10846          * Fires after a new set of Records has been loaded, before they are added to the store.
10847          * @param {Store} this
10848          * @param {Roo.data.Record[]} records The Records that were loaded
10849          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10850          * @params {Object} return from reader
10851          */
10852         load : true,
10853         /**
10854          * @event loadexception
10855          * Fires if an exception occurs in the Proxy during loading.
10856          * Called with the signature of the Proxy's "loadexception" event.
10857          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10858          * 
10859          * @param {Proxy} 
10860          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10861          * @param {Object} load options 
10862          * @param {Object} jsonData from your request (normally this contains the Exception)
10863          */
10864         loadexception : true
10865     });
10866     
10867     if(this.proxy){
10868         this.proxy = Roo.factory(this.proxy, Roo.data);
10869         this.proxy.xmodule = this.xmodule || false;
10870         this.relayEvents(this.proxy,  ["loadexception"]);
10871     }
10872     this.sortToggle = {};
10873     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10874
10875     Roo.data.Store.superclass.constructor.call(this);
10876
10877     if(this.inlineData){
10878         this.loadData(this.inlineData);
10879         delete this.inlineData;
10880     }
10881 };
10882
10883 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10884      /**
10885     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10886     * without a remote query - used by combo/forms at present.
10887     */
10888     
10889     /**
10890     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10891     */
10892     /**
10893     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10894     */
10895     /**
10896     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10897     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10898     */
10899     /**
10900     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10901     * on any HTTP request
10902     */
10903     /**
10904     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10905     */
10906     /**
10907     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10908     */
10909     multiSort: false,
10910     /**
10911     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10912     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10913     */
10914     remoteSort : false,
10915
10916     /**
10917     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10918      * loaded or when a record is removed. (defaults to false).
10919     */
10920     pruneModifiedRecords : false,
10921
10922     // private
10923     lastOptions : null,
10924
10925     /**
10926      * Add Records to the Store and fires the add event.
10927      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10928      */
10929     add : function(records){
10930         records = [].concat(records);
10931         for(var i = 0, len = records.length; i < len; i++){
10932             records[i].join(this);
10933         }
10934         var index = this.data.length;
10935         this.data.addAll(records);
10936         this.fireEvent("add", this, records, index);
10937     },
10938
10939     /**
10940      * Remove a Record from the Store and fires the remove event.
10941      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10942      */
10943     remove : function(record){
10944         var index = this.data.indexOf(record);
10945         this.data.removeAt(index);
10946         if(this.pruneModifiedRecords){
10947             this.modified.remove(record);
10948         }
10949         this.fireEvent("remove", this, record, index);
10950     },
10951
10952     /**
10953      * Remove all Records from the Store and fires the clear event.
10954      */
10955     removeAll : function(){
10956         this.data.clear();
10957         if(this.pruneModifiedRecords){
10958             this.modified = [];
10959         }
10960         this.fireEvent("clear", this);
10961     },
10962
10963     /**
10964      * Inserts Records to the Store at the given index and fires the add event.
10965      * @param {Number} index The start index at which to insert the passed Records.
10966      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10967      */
10968     insert : function(index, records){
10969         records = [].concat(records);
10970         for(var i = 0, len = records.length; i < len; i++){
10971             this.data.insert(index, records[i]);
10972             records[i].join(this);
10973         }
10974         this.fireEvent("add", this, records, index);
10975     },
10976
10977     /**
10978      * Get the index within the cache of the passed Record.
10979      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10980      * @return {Number} The index of the passed Record. Returns -1 if not found.
10981      */
10982     indexOf : function(record){
10983         return this.data.indexOf(record);
10984     },
10985
10986     /**
10987      * Get the index within the cache of the Record with the passed id.
10988      * @param {String} id The id of the Record to find.
10989      * @return {Number} The index of the Record. Returns -1 if not found.
10990      */
10991     indexOfId : function(id){
10992         return this.data.indexOfKey(id);
10993     },
10994
10995     /**
10996      * Get the Record with the specified id.
10997      * @param {String} id The id of the Record to find.
10998      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10999      */
11000     getById : function(id){
11001         return this.data.key(id);
11002     },
11003
11004     /**
11005      * Get the Record at the specified index.
11006      * @param {Number} index The index of the Record to find.
11007      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11008      */
11009     getAt : function(index){
11010         return this.data.itemAt(index);
11011     },
11012
11013     /**
11014      * Returns a range of Records between specified indices.
11015      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11016      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11017      * @return {Roo.data.Record[]} An array of Records
11018      */
11019     getRange : function(start, end){
11020         return this.data.getRange(start, end);
11021     },
11022
11023     // private
11024     storeOptions : function(o){
11025         o = Roo.apply({}, o);
11026         delete o.callback;
11027         delete o.scope;
11028         this.lastOptions = o;
11029     },
11030
11031     /**
11032      * Loads the Record cache from the configured Proxy using the configured Reader.
11033      * <p>
11034      * If using remote paging, then the first load call must specify the <em>start</em>
11035      * and <em>limit</em> properties in the options.params property to establish the initial
11036      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11037      * <p>
11038      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11039      * and this call will return before the new data has been loaded. Perform any post-processing
11040      * in a callback function, or in a "load" event handler.</strong>
11041      * <p>
11042      * @param {Object} options An object containing properties which control loading options:<ul>
11043      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11044      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11045      * passed the following arguments:<ul>
11046      * <li>r : Roo.data.Record[]</li>
11047      * <li>options: Options object from the load call</li>
11048      * <li>success: Boolean success indicator</li></ul></li>
11049      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11050      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11051      * </ul>
11052      */
11053     load : function(options){
11054         options = options || {};
11055         if(this.fireEvent("beforeload", this, options) !== false){
11056             this.storeOptions(options);
11057             var p = Roo.apply(options.params || {}, this.baseParams);
11058             // if meta was not loaded from remote source.. try requesting it.
11059             if (!this.reader.metaFromRemote) {
11060                 p._requestMeta = 1;
11061             }
11062             if(this.sortInfo && this.remoteSort){
11063                 var pn = this.paramNames;
11064                 p[pn["sort"]] = this.sortInfo.field;
11065                 p[pn["dir"]] = this.sortInfo.direction;
11066             }
11067             if (this.multiSort) {
11068                 var pn = this.paramNames;
11069                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11070             }
11071             
11072             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11073         }
11074     },
11075
11076     /**
11077      * Reloads the Record cache from the configured Proxy using the configured Reader and
11078      * the options from the last load operation performed.
11079      * @param {Object} options (optional) An object containing properties which may override the options
11080      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11081      * the most recently used options are reused).
11082      */
11083     reload : function(options){
11084         this.load(Roo.applyIf(options||{}, this.lastOptions));
11085     },
11086
11087     // private
11088     // Called as a callback by the Reader during a load operation.
11089     loadRecords : function(o, options, success){
11090         if(!o || success === false){
11091             if(success !== false){
11092                 this.fireEvent("load", this, [], options, o);
11093             }
11094             if(options.callback){
11095                 options.callback.call(options.scope || this, [], options, false);
11096             }
11097             return;
11098         }
11099         // if data returned failure - throw an exception.
11100         if (o.success === false) {
11101             // show a message if no listener is registered.
11102             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11103                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11104             }
11105             // loadmask wil be hooked into this..
11106             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11107             return;
11108         }
11109         var r = o.records, t = o.totalRecords || r.length;
11110         
11111         this.fireEvent("beforeloadadd", this, r, options, o);
11112         
11113         if(!options || options.add !== true){
11114             if(this.pruneModifiedRecords){
11115                 this.modified = [];
11116             }
11117             for(var i = 0, len = r.length; i < len; i++){
11118                 r[i].join(this);
11119             }
11120             if(this.snapshot){
11121                 this.data = this.snapshot;
11122                 delete this.snapshot;
11123             }
11124             this.data.clear();
11125             this.data.addAll(r);
11126             this.totalLength = t;
11127             this.applySort();
11128             this.fireEvent("datachanged", this);
11129         }else{
11130             this.totalLength = Math.max(t, this.data.length+r.length);
11131             this.add(r);
11132         }
11133         
11134         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11135                 
11136             var e = new Roo.data.Record({});
11137
11138             e.set(this.parent.displayField, this.parent.emptyTitle);
11139             e.set(this.parent.valueField, '');
11140
11141             this.insert(0, e);
11142         }
11143             
11144         this.fireEvent("load", this, r, options, o);
11145         if(options.callback){
11146             options.callback.call(options.scope || this, r, options, true);
11147         }
11148     },
11149
11150
11151     /**
11152      * Loads data from a passed data block. A Reader which understands the format of the data
11153      * must have been configured in the constructor.
11154      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11155      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11156      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11157      */
11158     loadData : function(o, append){
11159         var r = this.reader.readRecords(o);
11160         this.loadRecords(r, {add: append}, true);
11161     },
11162
11163     /**
11164      * Gets the number of cached records.
11165      * <p>
11166      * <em>If using paging, this may not be the total size of the dataset. If the data object
11167      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11168      * the data set size</em>
11169      */
11170     getCount : function(){
11171         return this.data.length || 0;
11172     },
11173
11174     /**
11175      * Gets the total number of records in the dataset as returned by the server.
11176      * <p>
11177      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11178      * the dataset size</em>
11179      */
11180     getTotalCount : function(){
11181         return this.totalLength || 0;
11182     },
11183
11184     /**
11185      * Returns the sort state of the Store as an object with two properties:
11186      * <pre><code>
11187  field {String} The name of the field by which the Records are sorted
11188  direction {String} The sort order, "ASC" or "DESC"
11189      * </code></pre>
11190      */
11191     getSortState : function(){
11192         return this.sortInfo;
11193     },
11194
11195     // private
11196     applySort : function(){
11197         if(this.sortInfo && !this.remoteSort){
11198             var s = this.sortInfo, f = s.field;
11199             var st = this.fields.get(f).sortType;
11200             var fn = function(r1, r2){
11201                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11202                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11203             };
11204             this.data.sort(s.direction, fn);
11205             if(this.snapshot && this.snapshot != this.data){
11206                 this.snapshot.sort(s.direction, fn);
11207             }
11208         }
11209     },
11210
11211     /**
11212      * Sets the default sort column and order to be used by the next load operation.
11213      * @param {String} fieldName The name of the field to sort by.
11214      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11215      */
11216     setDefaultSort : function(field, dir){
11217         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11218     },
11219
11220     /**
11221      * Sort the Records.
11222      * If remote sorting is used, the sort is performed on the server, and the cache is
11223      * reloaded. If local sorting is used, the cache is sorted internally.
11224      * @param {String} fieldName The name of the field to sort by.
11225      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11226      */
11227     sort : function(fieldName, dir){
11228         var f = this.fields.get(fieldName);
11229         if(!dir){
11230             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11231             
11232             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11233                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11234             }else{
11235                 dir = f.sortDir;
11236             }
11237         }
11238         this.sortToggle[f.name] = dir;
11239         this.sortInfo = {field: f.name, direction: dir};
11240         if(!this.remoteSort){
11241             this.applySort();
11242             this.fireEvent("datachanged", this);
11243         }else{
11244             this.load(this.lastOptions);
11245         }
11246     },
11247
11248     /**
11249      * Calls the specified function for each of the Records in the cache.
11250      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11251      * Returning <em>false</em> aborts and exits the iteration.
11252      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11253      */
11254     each : function(fn, scope){
11255         this.data.each(fn, scope);
11256     },
11257
11258     /**
11259      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11260      * (e.g., during paging).
11261      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11262      */
11263     getModifiedRecords : function(){
11264         return this.modified;
11265     },
11266
11267     // private
11268     createFilterFn : function(property, value, anyMatch){
11269         if(!value.exec){ // not a regex
11270             value = String(value);
11271             if(value.length == 0){
11272                 return false;
11273             }
11274             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11275         }
11276         return function(r){
11277             return value.test(r.data[property]);
11278         };
11279     },
11280
11281     /**
11282      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11283      * @param {String} property A field on your records
11284      * @param {Number} start The record index to start at (defaults to 0)
11285      * @param {Number} end The last record index to include (defaults to length - 1)
11286      * @return {Number} The sum
11287      */
11288     sum : function(property, start, end){
11289         var rs = this.data.items, v = 0;
11290         start = start || 0;
11291         end = (end || end === 0) ? end : rs.length-1;
11292
11293         for(var i = start; i <= end; i++){
11294             v += (rs[i].data[property] || 0);
11295         }
11296         return v;
11297     },
11298
11299     /**
11300      * Filter the records by a specified property.
11301      * @param {String} field A field on your records
11302      * @param {String/RegExp} value Either a string that the field
11303      * should start with or a RegExp to test against the field
11304      * @param {Boolean} anyMatch True to match any part not just the beginning
11305      */
11306     filter : function(property, value, anyMatch){
11307         var fn = this.createFilterFn(property, value, anyMatch);
11308         return fn ? this.filterBy(fn) : this.clearFilter();
11309     },
11310
11311     /**
11312      * Filter by a function. The specified function will be called with each
11313      * record in this data source. If the function returns true the record is included,
11314      * otherwise it is filtered.
11315      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11316      * @param {Object} scope (optional) The scope of the function (defaults to this)
11317      */
11318     filterBy : function(fn, scope){
11319         this.snapshot = this.snapshot || this.data;
11320         this.data = this.queryBy(fn, scope||this);
11321         this.fireEvent("datachanged", this);
11322     },
11323
11324     /**
11325      * Query the records by a specified property.
11326      * @param {String} field A field on your records
11327      * @param {String/RegExp} value Either a string that the field
11328      * should start with or a RegExp to test against the field
11329      * @param {Boolean} anyMatch True to match any part not just the beginning
11330      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11331      */
11332     query : function(property, value, anyMatch){
11333         var fn = this.createFilterFn(property, value, anyMatch);
11334         return fn ? this.queryBy(fn) : this.data.clone();
11335     },
11336
11337     /**
11338      * Query by a function. The specified function will be called with each
11339      * record in this data source. If the function returns true the record is included
11340      * in the results.
11341      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11342      * @param {Object} scope (optional) The scope of the function (defaults to this)
11343       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11344      **/
11345     queryBy : function(fn, scope){
11346         var data = this.snapshot || this.data;
11347         return data.filterBy(fn, scope||this);
11348     },
11349
11350     /**
11351      * Collects unique values for a particular dataIndex from this store.
11352      * @param {String} dataIndex The property to collect
11353      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11354      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11355      * @return {Array} An array of the unique values
11356      **/
11357     collect : function(dataIndex, allowNull, bypassFilter){
11358         var d = (bypassFilter === true && this.snapshot) ?
11359                 this.snapshot.items : this.data.items;
11360         var v, sv, r = [], l = {};
11361         for(var i = 0, len = d.length; i < len; i++){
11362             v = d[i].data[dataIndex];
11363             sv = String(v);
11364             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11365                 l[sv] = true;
11366                 r[r.length] = v;
11367             }
11368         }
11369         return r;
11370     },
11371
11372     /**
11373      * Revert to a view of the Record cache with no filtering applied.
11374      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11375      */
11376     clearFilter : function(suppressEvent){
11377         if(this.snapshot && this.snapshot != this.data){
11378             this.data = this.snapshot;
11379             delete this.snapshot;
11380             if(suppressEvent !== true){
11381                 this.fireEvent("datachanged", this);
11382             }
11383         }
11384     },
11385
11386     // private
11387     afterEdit : function(record){
11388         if(this.modified.indexOf(record) == -1){
11389             this.modified.push(record);
11390         }
11391         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11392     },
11393     
11394     // private
11395     afterReject : function(record){
11396         this.modified.remove(record);
11397         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11398     },
11399
11400     // private
11401     afterCommit : function(record){
11402         this.modified.remove(record);
11403         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11404     },
11405
11406     /**
11407      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11408      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11409      */
11410     commitChanges : function(){
11411         var m = this.modified.slice(0);
11412         this.modified = [];
11413         for(var i = 0, len = m.length; i < len; i++){
11414             m[i].commit();
11415         }
11416     },
11417
11418     /**
11419      * Cancel outstanding changes on all changed records.
11420      */
11421     rejectChanges : function(){
11422         var m = this.modified.slice(0);
11423         this.modified = [];
11424         for(var i = 0, len = m.length; i < len; i++){
11425             m[i].reject();
11426         }
11427     },
11428
11429     onMetaChange : function(meta, rtype, o){
11430         this.recordType = rtype;
11431         this.fields = rtype.prototype.fields;
11432         delete this.snapshot;
11433         this.sortInfo = meta.sortInfo || this.sortInfo;
11434         this.modified = [];
11435         this.fireEvent('metachange', this, this.reader.meta);
11436     },
11437     
11438     moveIndex : function(data, type)
11439     {
11440         var index = this.indexOf(data);
11441         
11442         var newIndex = index + type;
11443         
11444         this.remove(data);
11445         
11446         this.insert(newIndex, data);
11447         
11448     }
11449 });/*
11450  * Based on:
11451  * Ext JS Library 1.1.1
11452  * Copyright(c) 2006-2007, Ext JS, LLC.
11453  *
11454  * Originally Released Under LGPL - original licence link has changed is not relivant.
11455  *
11456  * Fork - LGPL
11457  * <script type="text/javascript">
11458  */
11459
11460 /**
11461  * @class Roo.data.SimpleStore
11462  * @extends Roo.data.Store
11463  * Small helper class to make creating Stores from Array data easier.
11464  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11465  * @cfg {Array} fields An array of field definition objects, or field name strings.
11466  * @cfg {Array} data The multi-dimensional array of data
11467  * @constructor
11468  * @param {Object} config
11469  */
11470 Roo.data.SimpleStore = function(config){
11471     Roo.data.SimpleStore.superclass.constructor.call(this, {
11472         isLocal : true,
11473         reader: new Roo.data.ArrayReader({
11474                 id: config.id
11475             },
11476             Roo.data.Record.create(config.fields)
11477         ),
11478         proxy : new Roo.data.MemoryProxy(config.data)
11479     });
11480     this.load();
11481 };
11482 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11483  * Based on:
11484  * Ext JS Library 1.1.1
11485  * Copyright(c) 2006-2007, Ext JS, LLC.
11486  *
11487  * Originally Released Under LGPL - original licence link has changed is not relivant.
11488  *
11489  * Fork - LGPL
11490  * <script type="text/javascript">
11491  */
11492
11493 /**
11494 /**
11495  * @extends Roo.data.Store
11496  * @class Roo.data.JsonStore
11497  * Small helper class to make creating Stores for JSON data easier. <br/>
11498 <pre><code>
11499 var store = new Roo.data.JsonStore({
11500     url: 'get-images.php',
11501     root: 'images',
11502     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11503 });
11504 </code></pre>
11505  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11506  * JsonReader and HttpProxy (unless inline data is provided).</b>
11507  * @cfg {Array} fields An array of field definition objects, or field name strings.
11508  * @constructor
11509  * @param {Object} config
11510  */
11511 Roo.data.JsonStore = function(c){
11512     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11513         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11514         reader: new Roo.data.JsonReader(c, c.fields)
11515     }));
11516 };
11517 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11518  * Based on:
11519  * Ext JS Library 1.1.1
11520  * Copyright(c) 2006-2007, Ext JS, LLC.
11521  *
11522  * Originally Released Under LGPL - original licence link has changed is not relivant.
11523  *
11524  * Fork - LGPL
11525  * <script type="text/javascript">
11526  */
11527
11528  
11529 Roo.data.Field = function(config){
11530     if(typeof config == "string"){
11531         config = {name: config};
11532     }
11533     Roo.apply(this, config);
11534     
11535     if(!this.type){
11536         this.type = "auto";
11537     }
11538     
11539     var st = Roo.data.SortTypes;
11540     // named sortTypes are supported, here we look them up
11541     if(typeof this.sortType == "string"){
11542         this.sortType = st[this.sortType];
11543     }
11544     
11545     // set default sortType for strings and dates
11546     if(!this.sortType){
11547         switch(this.type){
11548             case "string":
11549                 this.sortType = st.asUCString;
11550                 break;
11551             case "date":
11552                 this.sortType = st.asDate;
11553                 break;
11554             default:
11555                 this.sortType = st.none;
11556         }
11557     }
11558
11559     // define once
11560     var stripRe = /[\$,%]/g;
11561
11562     // prebuilt conversion function for this field, instead of
11563     // switching every time we're reading a value
11564     if(!this.convert){
11565         var cv, dateFormat = this.dateFormat;
11566         switch(this.type){
11567             case "":
11568             case "auto":
11569             case undefined:
11570                 cv = function(v){ return v; };
11571                 break;
11572             case "string":
11573                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11574                 break;
11575             case "int":
11576                 cv = function(v){
11577                     return v !== undefined && v !== null && v !== '' ?
11578                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11579                     };
11580                 break;
11581             case "float":
11582                 cv = function(v){
11583                     return v !== undefined && v !== null && v !== '' ?
11584                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11585                     };
11586                 break;
11587             case "bool":
11588             case "boolean":
11589                 cv = function(v){ return v === true || v === "true" || v == 1; };
11590                 break;
11591             case "date":
11592                 cv = function(v){
11593                     if(!v){
11594                         return '';
11595                     }
11596                     if(v instanceof Date){
11597                         return v;
11598                     }
11599                     if(dateFormat){
11600                         if(dateFormat == "timestamp"){
11601                             return new Date(v*1000);
11602                         }
11603                         return Date.parseDate(v, dateFormat);
11604                     }
11605                     var parsed = Date.parse(v);
11606                     return parsed ? new Date(parsed) : null;
11607                 };
11608              break;
11609             
11610         }
11611         this.convert = cv;
11612     }
11613 };
11614
11615 Roo.data.Field.prototype = {
11616     dateFormat: null,
11617     defaultValue: "",
11618     mapping: null,
11619     sortType : null,
11620     sortDir : "ASC"
11621 };/*
11622  * Based on:
11623  * Ext JS Library 1.1.1
11624  * Copyright(c) 2006-2007, Ext JS, LLC.
11625  *
11626  * Originally Released Under LGPL - original licence link has changed is not relivant.
11627  *
11628  * Fork - LGPL
11629  * <script type="text/javascript">
11630  */
11631  
11632 // Base class for reading structured data from a data source.  This class is intended to be
11633 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11634
11635 /**
11636  * @class Roo.data.DataReader
11637  * Base class for reading structured data from a data source.  This class is intended to be
11638  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11639  */
11640
11641 Roo.data.DataReader = function(meta, recordType){
11642     
11643     this.meta = meta;
11644     
11645     this.recordType = recordType instanceof Array ? 
11646         Roo.data.Record.create(recordType) : recordType;
11647 };
11648
11649 Roo.data.DataReader.prototype = {
11650      /**
11651      * Create an empty record
11652      * @param {Object} data (optional) - overlay some values
11653      * @return {Roo.data.Record} record created.
11654      */
11655     newRow :  function(d) {
11656         var da =  {};
11657         this.recordType.prototype.fields.each(function(c) {
11658             switch( c.type) {
11659                 case 'int' : da[c.name] = 0; break;
11660                 case 'date' : da[c.name] = new Date(); break;
11661                 case 'float' : da[c.name] = 0.0; break;
11662                 case 'boolean' : da[c.name] = false; break;
11663                 default : da[c.name] = ""; break;
11664             }
11665             
11666         });
11667         return new this.recordType(Roo.apply(da, d));
11668     }
11669     
11670 };/*
11671  * Based on:
11672  * Ext JS Library 1.1.1
11673  * Copyright(c) 2006-2007, Ext JS, LLC.
11674  *
11675  * Originally Released Under LGPL - original licence link has changed is not relivant.
11676  *
11677  * Fork - LGPL
11678  * <script type="text/javascript">
11679  */
11680
11681 /**
11682  * @class Roo.data.DataProxy
11683  * @extends Roo.data.Observable
11684  * This class is an abstract base class for implementations which provide retrieval of
11685  * unformatted data objects.<br>
11686  * <p>
11687  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11688  * (of the appropriate type which knows how to parse the data object) to provide a block of
11689  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11690  * <p>
11691  * Custom implementations must implement the load method as described in
11692  * {@link Roo.data.HttpProxy#load}.
11693  */
11694 Roo.data.DataProxy = function(){
11695     this.addEvents({
11696         /**
11697          * @event beforeload
11698          * Fires before a network request is made to retrieve a data object.
11699          * @param {Object} This DataProxy object.
11700          * @param {Object} params The params parameter to the load function.
11701          */
11702         beforeload : true,
11703         /**
11704          * @event load
11705          * Fires before the load method's callback is called.
11706          * @param {Object} This DataProxy object.
11707          * @param {Object} o The data object.
11708          * @param {Object} arg The callback argument object passed to the load function.
11709          */
11710         load : true,
11711         /**
11712          * @event loadexception
11713          * Fires if an Exception occurs during data retrieval.
11714          * @param {Object} This DataProxy object.
11715          * @param {Object} o The data object.
11716          * @param {Object} arg The callback argument object passed to the load function.
11717          * @param {Object} e The Exception.
11718          */
11719         loadexception : true
11720     });
11721     Roo.data.DataProxy.superclass.constructor.call(this);
11722 };
11723
11724 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11725
11726     /**
11727      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11728      */
11729 /*
11730  * Based on:
11731  * Ext JS Library 1.1.1
11732  * Copyright(c) 2006-2007, Ext JS, LLC.
11733  *
11734  * Originally Released Under LGPL - original licence link has changed is not relivant.
11735  *
11736  * Fork - LGPL
11737  * <script type="text/javascript">
11738  */
11739 /**
11740  * @class Roo.data.MemoryProxy
11741  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11742  * to the Reader when its load method is called.
11743  * @constructor
11744  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11745  */
11746 Roo.data.MemoryProxy = function(data){
11747     if (data.data) {
11748         data = data.data;
11749     }
11750     Roo.data.MemoryProxy.superclass.constructor.call(this);
11751     this.data = data;
11752 };
11753
11754 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11755     
11756     /**
11757      * Load data from the requested source (in this case an in-memory
11758      * data object passed to the constructor), read the data object into
11759      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11760      * process that block using the passed callback.
11761      * @param {Object} params This parameter is not used by the MemoryProxy class.
11762      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11763      * object into a block of Roo.data.Records.
11764      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11765      * The function must be passed <ul>
11766      * <li>The Record block object</li>
11767      * <li>The "arg" argument from the load function</li>
11768      * <li>A boolean success indicator</li>
11769      * </ul>
11770      * @param {Object} scope The scope in which to call the callback
11771      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11772      */
11773     load : function(params, reader, callback, scope, arg){
11774         params = params || {};
11775         var result;
11776         try {
11777             result = reader.readRecords(this.data);
11778         }catch(e){
11779             this.fireEvent("loadexception", this, arg, null, e);
11780             callback.call(scope, null, arg, false);
11781             return;
11782         }
11783         callback.call(scope, result, arg, true);
11784     },
11785     
11786     // private
11787     update : function(params, records){
11788         
11789     }
11790 });/*
11791  * Based on:
11792  * Ext JS Library 1.1.1
11793  * Copyright(c) 2006-2007, Ext JS, LLC.
11794  *
11795  * Originally Released Under LGPL - original licence link has changed is not relivant.
11796  *
11797  * Fork - LGPL
11798  * <script type="text/javascript">
11799  */
11800 /**
11801  * @class Roo.data.HttpProxy
11802  * @extends Roo.data.DataProxy
11803  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11804  * configured to reference a certain URL.<br><br>
11805  * <p>
11806  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11807  * from which the running page was served.<br><br>
11808  * <p>
11809  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11810  * <p>
11811  * Be aware that to enable the browser to parse an XML document, the server must set
11812  * the Content-Type header in the HTTP response to "text/xml".
11813  * @constructor
11814  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11815  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11816  * will be used to make the request.
11817  */
11818 Roo.data.HttpProxy = function(conn){
11819     Roo.data.HttpProxy.superclass.constructor.call(this);
11820     // is conn a conn config or a real conn?
11821     this.conn = conn;
11822     this.useAjax = !conn || !conn.events;
11823   
11824 };
11825
11826 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11827     // thse are take from connection...
11828     
11829     /**
11830      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11831      */
11832     /**
11833      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11834      * extra parameters to each request made by this object. (defaults to undefined)
11835      */
11836     /**
11837      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11838      *  to each request made by this object. (defaults to undefined)
11839      */
11840     /**
11841      * @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)
11842      */
11843     /**
11844      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11845      */
11846      /**
11847      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11848      * @type Boolean
11849      */
11850   
11851
11852     /**
11853      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11854      * @type Boolean
11855      */
11856     /**
11857      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11858      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11859      * a finer-grained basis than the DataProxy events.
11860      */
11861     getConnection : function(){
11862         return this.useAjax ? Roo.Ajax : this.conn;
11863     },
11864
11865     /**
11866      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11867      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11868      * process that block using the passed callback.
11869      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11870      * for the request to the remote server.
11871      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11872      * object into a block of Roo.data.Records.
11873      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11874      * The function must be passed <ul>
11875      * <li>The Record block object</li>
11876      * <li>The "arg" argument from the load function</li>
11877      * <li>A boolean success indicator</li>
11878      * </ul>
11879      * @param {Object} scope The scope in which to call the callback
11880      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11881      */
11882     load : function(params, reader, callback, scope, arg){
11883         if(this.fireEvent("beforeload", this, params) !== false){
11884             var  o = {
11885                 params : params || {},
11886                 request: {
11887                     callback : callback,
11888                     scope : scope,
11889                     arg : arg
11890                 },
11891                 reader: reader,
11892                 callback : this.loadResponse,
11893                 scope: this
11894             };
11895             if(this.useAjax){
11896                 Roo.applyIf(o, this.conn);
11897                 if(this.activeRequest){
11898                     Roo.Ajax.abort(this.activeRequest);
11899                 }
11900                 this.activeRequest = Roo.Ajax.request(o);
11901             }else{
11902                 this.conn.request(o);
11903             }
11904         }else{
11905             callback.call(scope||this, null, arg, false);
11906         }
11907     },
11908
11909     // private
11910     loadResponse : function(o, success, response){
11911         delete this.activeRequest;
11912         if(!success){
11913             this.fireEvent("loadexception", this, o, response);
11914             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11915             return;
11916         }
11917         var result;
11918         try {
11919             result = o.reader.read(response);
11920         }catch(e){
11921             this.fireEvent("loadexception", this, o, response, e);
11922             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11923             return;
11924         }
11925         
11926         this.fireEvent("load", this, o, o.request.arg);
11927         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11928     },
11929
11930     // private
11931     update : function(dataSet){
11932
11933     },
11934
11935     // private
11936     updateResponse : function(dataSet){
11937
11938     }
11939 });/*
11940  * Based on:
11941  * Ext JS Library 1.1.1
11942  * Copyright(c) 2006-2007, Ext JS, LLC.
11943  *
11944  * Originally Released Under LGPL - original licence link has changed is not relivant.
11945  *
11946  * Fork - LGPL
11947  * <script type="text/javascript">
11948  */
11949
11950 /**
11951  * @class Roo.data.ScriptTagProxy
11952  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11953  * other than the originating domain of the running page.<br><br>
11954  * <p>
11955  * <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
11956  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11957  * <p>
11958  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11959  * source code that is used as the source inside a &lt;script> tag.<br><br>
11960  * <p>
11961  * In order for the browser to process the returned data, the server must wrap the data object
11962  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11963  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11964  * depending on whether the callback name was passed:
11965  * <p>
11966  * <pre><code>
11967 boolean scriptTag = false;
11968 String cb = request.getParameter("callback");
11969 if (cb != null) {
11970     scriptTag = true;
11971     response.setContentType("text/javascript");
11972 } else {
11973     response.setContentType("application/x-json");
11974 }
11975 Writer out = response.getWriter();
11976 if (scriptTag) {
11977     out.write(cb + "(");
11978 }
11979 out.print(dataBlock.toJsonString());
11980 if (scriptTag) {
11981     out.write(");");
11982 }
11983 </pre></code>
11984  *
11985  * @constructor
11986  * @param {Object} config A configuration object.
11987  */
11988 Roo.data.ScriptTagProxy = function(config){
11989     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11990     Roo.apply(this, config);
11991     this.head = document.getElementsByTagName("head")[0];
11992 };
11993
11994 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11995
11996 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11997     /**
11998      * @cfg {String} url The URL from which to request the data object.
11999      */
12000     /**
12001      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12002      */
12003     timeout : 30000,
12004     /**
12005      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12006      * the server the name of the callback function set up by the load call to process the returned data object.
12007      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12008      * javascript output which calls this named function passing the data object as its only parameter.
12009      */
12010     callbackParam : "callback",
12011     /**
12012      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12013      * name to the request.
12014      */
12015     nocache : true,
12016
12017     /**
12018      * Load data from the configured URL, read the data object into
12019      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12020      * process that block using the passed callback.
12021      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12022      * for the request to the remote server.
12023      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12024      * object into a block of Roo.data.Records.
12025      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12026      * The function must be passed <ul>
12027      * <li>The Record block object</li>
12028      * <li>The "arg" argument from the load function</li>
12029      * <li>A boolean success indicator</li>
12030      * </ul>
12031      * @param {Object} scope The scope in which to call the callback
12032      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12033      */
12034     load : function(params, reader, callback, scope, arg){
12035         if(this.fireEvent("beforeload", this, params) !== false){
12036
12037             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12038
12039             var url = this.url;
12040             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12041             if(this.nocache){
12042                 url += "&_dc=" + (new Date().getTime());
12043             }
12044             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12045             var trans = {
12046                 id : transId,
12047                 cb : "stcCallback"+transId,
12048                 scriptId : "stcScript"+transId,
12049                 params : params,
12050                 arg : arg,
12051                 url : url,
12052                 callback : callback,
12053                 scope : scope,
12054                 reader : reader
12055             };
12056             var conn = this;
12057
12058             window[trans.cb] = function(o){
12059                 conn.handleResponse(o, trans);
12060             };
12061
12062             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12063
12064             if(this.autoAbort !== false){
12065                 this.abort();
12066             }
12067
12068             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12069
12070             var script = document.createElement("script");
12071             script.setAttribute("src", url);
12072             script.setAttribute("type", "text/javascript");
12073             script.setAttribute("id", trans.scriptId);
12074             this.head.appendChild(script);
12075
12076             this.trans = trans;
12077         }else{
12078             callback.call(scope||this, null, arg, false);
12079         }
12080     },
12081
12082     // private
12083     isLoading : function(){
12084         return this.trans ? true : false;
12085     },
12086
12087     /**
12088      * Abort the current server request.
12089      */
12090     abort : function(){
12091         if(this.isLoading()){
12092             this.destroyTrans(this.trans);
12093         }
12094     },
12095
12096     // private
12097     destroyTrans : function(trans, isLoaded){
12098         this.head.removeChild(document.getElementById(trans.scriptId));
12099         clearTimeout(trans.timeoutId);
12100         if(isLoaded){
12101             window[trans.cb] = undefined;
12102             try{
12103                 delete window[trans.cb];
12104             }catch(e){}
12105         }else{
12106             // if hasn't been loaded, wait for load to remove it to prevent script error
12107             window[trans.cb] = function(){
12108                 window[trans.cb] = undefined;
12109                 try{
12110                     delete window[trans.cb];
12111                 }catch(e){}
12112             };
12113         }
12114     },
12115
12116     // private
12117     handleResponse : function(o, trans){
12118         this.trans = false;
12119         this.destroyTrans(trans, true);
12120         var result;
12121         try {
12122             result = trans.reader.readRecords(o);
12123         }catch(e){
12124             this.fireEvent("loadexception", this, o, trans.arg, e);
12125             trans.callback.call(trans.scope||window, null, trans.arg, false);
12126             return;
12127         }
12128         this.fireEvent("load", this, o, trans.arg);
12129         trans.callback.call(trans.scope||window, result, trans.arg, true);
12130     },
12131
12132     // private
12133     handleFailure : function(trans){
12134         this.trans = false;
12135         this.destroyTrans(trans, false);
12136         this.fireEvent("loadexception", this, null, trans.arg);
12137         trans.callback.call(trans.scope||window, null, trans.arg, false);
12138     }
12139 });/*
12140  * Based on:
12141  * Ext JS Library 1.1.1
12142  * Copyright(c) 2006-2007, Ext JS, LLC.
12143  *
12144  * Originally Released Under LGPL - original licence link has changed is not relivant.
12145  *
12146  * Fork - LGPL
12147  * <script type="text/javascript">
12148  */
12149
12150 /**
12151  * @class Roo.data.JsonReader
12152  * @extends Roo.data.DataReader
12153  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12154  * based on mappings in a provided Roo.data.Record constructor.
12155  * 
12156  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12157  * in the reply previously. 
12158  * 
12159  * <p>
12160  * Example code:
12161  * <pre><code>
12162 var RecordDef = Roo.data.Record.create([
12163     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12164     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12165 ]);
12166 var myReader = new Roo.data.JsonReader({
12167     totalProperty: "results",    // The property which contains the total dataset size (optional)
12168     root: "rows",                // The property which contains an Array of row objects
12169     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12170 }, RecordDef);
12171 </code></pre>
12172  * <p>
12173  * This would consume a JSON file like this:
12174  * <pre><code>
12175 { 'results': 2, 'rows': [
12176     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12177     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12178 }
12179 </code></pre>
12180  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12181  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12182  * paged from the remote server.
12183  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12184  * @cfg {String} root name of the property which contains the Array of row objects.
12185  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12186  * @cfg {Array} fields Array of field definition objects
12187  * @constructor
12188  * Create a new JsonReader
12189  * @param {Object} meta Metadata configuration options
12190  * @param {Object} recordType Either an Array of field definition objects,
12191  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12192  */
12193 Roo.data.JsonReader = function(meta, recordType){
12194     
12195     meta = meta || {};
12196     // set some defaults:
12197     Roo.applyIf(meta, {
12198         totalProperty: 'total',
12199         successProperty : 'success',
12200         root : 'data',
12201         id : 'id'
12202     });
12203     
12204     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12205 };
12206 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12207     
12208     /**
12209      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12210      * Used by Store query builder to append _requestMeta to params.
12211      * 
12212      */
12213     metaFromRemote : false,
12214     /**
12215      * This method is only used by a DataProxy which has retrieved data from a remote server.
12216      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12217      * @return {Object} data A data block which is used by an Roo.data.Store object as
12218      * a cache of Roo.data.Records.
12219      */
12220     read : function(response){
12221         var json = response.responseText;
12222        
12223         var o = /* eval:var:o */ eval("("+json+")");
12224         if(!o) {
12225             throw {message: "JsonReader.read: Json object not found"};
12226         }
12227         
12228         if(o.metaData){
12229             
12230             delete this.ef;
12231             this.metaFromRemote = true;
12232             this.meta = o.metaData;
12233             this.recordType = Roo.data.Record.create(o.metaData.fields);
12234             this.onMetaChange(this.meta, this.recordType, o);
12235         }
12236         return this.readRecords(o);
12237     },
12238
12239     // private function a store will implement
12240     onMetaChange : function(meta, recordType, o){
12241
12242     },
12243
12244     /**
12245          * @ignore
12246          */
12247     simpleAccess: function(obj, subsc) {
12248         return obj[subsc];
12249     },
12250
12251         /**
12252          * @ignore
12253          */
12254     getJsonAccessor: function(){
12255         var re = /[\[\.]/;
12256         return function(expr) {
12257             try {
12258                 return(re.test(expr))
12259                     ? new Function("obj", "return obj." + expr)
12260                     : function(obj){
12261                         return obj[expr];
12262                     };
12263             } catch(e){}
12264             return Roo.emptyFn;
12265         };
12266     }(),
12267
12268     /**
12269      * Create a data block containing Roo.data.Records from an XML document.
12270      * @param {Object} o An object which contains an Array of row objects in the property specified
12271      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12272      * which contains the total size of the dataset.
12273      * @return {Object} data A data block which is used by an Roo.data.Store object as
12274      * a cache of Roo.data.Records.
12275      */
12276     readRecords : function(o){
12277         /**
12278          * After any data loads, the raw JSON data is available for further custom processing.
12279          * @type Object
12280          */
12281         this.o = o;
12282         var s = this.meta, Record = this.recordType,
12283             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12284
12285 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12286         if (!this.ef) {
12287             if(s.totalProperty) {
12288                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12289                 }
12290                 if(s.successProperty) {
12291                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12292                 }
12293                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12294                 if (s.id) {
12295                         var g = this.getJsonAccessor(s.id);
12296                         this.getId = function(rec) {
12297                                 var r = g(rec);  
12298                                 return (r === undefined || r === "") ? null : r;
12299                         };
12300                 } else {
12301                         this.getId = function(){return null;};
12302                 }
12303             this.ef = [];
12304             for(var jj = 0; jj < fl; jj++){
12305                 f = fi[jj];
12306                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12307                 this.ef[jj] = this.getJsonAccessor(map);
12308             }
12309         }
12310
12311         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12312         if(s.totalProperty){
12313             var vt = parseInt(this.getTotal(o), 10);
12314             if(!isNaN(vt)){
12315                 totalRecords = vt;
12316             }
12317         }
12318         if(s.successProperty){
12319             var vs = this.getSuccess(o);
12320             if(vs === false || vs === 'false'){
12321                 success = false;
12322             }
12323         }
12324         var records = [];
12325         for(var i = 0; i < c; i++){
12326                 var n = root[i];
12327             var values = {};
12328             var id = this.getId(n);
12329             for(var j = 0; j < fl; j++){
12330                 f = fi[j];
12331             var v = this.ef[j](n);
12332             if (!f.convert) {
12333                 Roo.log('missing convert for ' + f.name);
12334                 Roo.log(f);
12335                 continue;
12336             }
12337             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12338             }
12339             var record = new Record(values, id);
12340             record.json = n;
12341             records[i] = record;
12342         }
12343         return {
12344             raw : o,
12345             success : success,
12346             records : records,
12347             totalRecords : totalRecords
12348         };
12349     }
12350 });/*
12351  * Based on:
12352  * Ext JS Library 1.1.1
12353  * Copyright(c) 2006-2007, Ext JS, LLC.
12354  *
12355  * Originally Released Under LGPL - original licence link has changed is not relivant.
12356  *
12357  * Fork - LGPL
12358  * <script type="text/javascript">
12359  */
12360
12361 /**
12362  * @class Roo.data.ArrayReader
12363  * @extends Roo.data.DataReader
12364  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12365  * Each element of that Array represents a row of data fields. The
12366  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12367  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12368  * <p>
12369  * Example code:.
12370  * <pre><code>
12371 var RecordDef = Roo.data.Record.create([
12372     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12373     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12374 ]);
12375 var myReader = new Roo.data.ArrayReader({
12376     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12377 }, RecordDef);
12378 </code></pre>
12379  * <p>
12380  * This would consume an Array like this:
12381  * <pre><code>
12382 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12383   </code></pre>
12384  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12385  * @constructor
12386  * Create a new JsonReader
12387  * @param {Object} meta Metadata configuration options.
12388  * @param {Object} recordType Either an Array of field definition objects
12389  * as specified to {@link Roo.data.Record#create},
12390  * or an {@link Roo.data.Record} object
12391  * created using {@link Roo.data.Record#create}.
12392  */
12393 Roo.data.ArrayReader = function(meta, recordType){
12394     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12395 };
12396
12397 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12398     /**
12399      * Create a data block containing Roo.data.Records from an XML document.
12400      * @param {Object} o An Array of row objects which represents the dataset.
12401      * @return {Object} data A data block which is used by an Roo.data.Store object as
12402      * a cache of Roo.data.Records.
12403      */
12404     readRecords : function(o){
12405         var sid = this.meta ? this.meta.id : null;
12406         var recordType = this.recordType, fields = recordType.prototype.fields;
12407         var records = [];
12408         var root = o;
12409             for(var i = 0; i < root.length; i++){
12410                     var n = root[i];
12411                 var values = {};
12412                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12413                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12414                 var f = fields.items[j];
12415                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12416                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12417                 v = f.convert(v);
12418                 values[f.name] = v;
12419             }
12420                 var record = new recordType(values, id);
12421                 record.json = n;
12422                 records[records.length] = record;
12423             }
12424             return {
12425                 records : records,
12426                 totalRecords : records.length
12427             };
12428     }
12429 });/*
12430  * - LGPL
12431  * * 
12432  */
12433
12434 /**
12435  * @class Roo.bootstrap.ComboBox
12436  * @extends Roo.bootstrap.TriggerField
12437  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12438  * @cfg {Boolean} append (true|false) default false
12439  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12440  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12441  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12442  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12443  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12444  * @cfg {Boolean} animate default true
12445  * @cfg {Boolean} emptyResultText only for touch device
12446  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12447  * @cfg {String} emptyTitle default ''
12448  * @constructor
12449  * Create a new ComboBox.
12450  * @param {Object} config Configuration options
12451  */
12452 Roo.bootstrap.ComboBox = function(config){
12453     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12454     this.addEvents({
12455         /**
12456          * @event expand
12457          * Fires when the dropdown list is expanded
12458         * @param {Roo.bootstrap.ComboBox} combo This combo box
12459         */
12460         'expand' : true,
12461         /**
12462          * @event collapse
12463          * Fires when the dropdown list is collapsed
12464         * @param {Roo.bootstrap.ComboBox} combo This combo box
12465         */
12466         'collapse' : true,
12467         /**
12468          * @event beforeselect
12469          * Fires before a list item is selected. Return false to cancel the selection.
12470         * @param {Roo.bootstrap.ComboBox} combo This combo box
12471         * @param {Roo.data.Record} record The data record returned from the underlying store
12472         * @param {Number} index The index of the selected item in the dropdown list
12473         */
12474         'beforeselect' : true,
12475         /**
12476          * @event select
12477          * Fires when a list item is selected
12478         * @param {Roo.bootstrap.ComboBox} combo This combo box
12479         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12480         * @param {Number} index The index of the selected item in the dropdown list
12481         */
12482         'select' : true,
12483         /**
12484          * @event beforequery
12485          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12486          * The event object passed has these properties:
12487         * @param {Roo.bootstrap.ComboBox} combo This combo box
12488         * @param {String} query The query
12489         * @param {Boolean} forceAll true to force "all" query
12490         * @param {Boolean} cancel true to cancel the query
12491         * @param {Object} e The query event object
12492         */
12493         'beforequery': true,
12494          /**
12495          * @event add
12496          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12497         * @param {Roo.bootstrap.ComboBox} combo This combo box
12498         */
12499         'add' : true,
12500         /**
12501          * @event edit
12502          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12503         * @param {Roo.bootstrap.ComboBox} combo This combo box
12504         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12505         */
12506         'edit' : true,
12507         /**
12508          * @event remove
12509          * Fires when the remove value from the combobox array
12510         * @param {Roo.bootstrap.ComboBox} combo This combo box
12511         */
12512         'remove' : true,
12513         /**
12514          * @event afterremove
12515          * Fires when the remove value from the combobox array
12516         * @param {Roo.bootstrap.ComboBox} combo This combo box
12517         */
12518         'afterremove' : true,
12519         /**
12520          * @event specialfilter
12521          * Fires when specialfilter
12522             * @param {Roo.bootstrap.ComboBox} combo This combo box
12523             */
12524         'specialfilter' : true,
12525         /**
12526          * @event tick
12527          * Fires when tick the element
12528             * @param {Roo.bootstrap.ComboBox} combo This combo box
12529             */
12530         'tick' : true,
12531         /**
12532          * @event touchviewdisplay
12533          * Fires when touch view require special display (default is using displayField)
12534             * @param {Roo.bootstrap.ComboBox} combo This combo box
12535             * @param {Object} cfg set html .
12536             */
12537         'touchviewdisplay' : true
12538         
12539     });
12540     
12541     this.item = [];
12542     this.tickItems = [];
12543     
12544     this.selectedIndex = -1;
12545     if(this.mode == 'local'){
12546         if(config.queryDelay === undefined){
12547             this.queryDelay = 10;
12548         }
12549         if(config.minChars === undefined){
12550             this.minChars = 0;
12551         }
12552     }
12553 };
12554
12555 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12556      
12557     /**
12558      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12559      * rendering into an Roo.Editor, defaults to false)
12560      */
12561     /**
12562      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12563      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12564      */
12565     /**
12566      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12567      */
12568     /**
12569      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12570      * the dropdown list (defaults to undefined, with no header element)
12571      */
12572
12573      /**
12574      * @cfg {String/Roo.Template} tpl The template to use to render the output
12575      */
12576      
12577      /**
12578      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12579      */
12580     listWidth: undefined,
12581     /**
12582      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12583      * mode = 'remote' or 'text' if mode = 'local')
12584      */
12585     displayField: undefined,
12586     
12587     /**
12588      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12589      * mode = 'remote' or 'value' if mode = 'local'). 
12590      * Note: use of a valueField requires the user make a selection
12591      * in order for a value to be mapped.
12592      */
12593     valueField: undefined,
12594     /**
12595      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12596      */
12597     modalTitle : '',
12598     
12599     /**
12600      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12601      * field's data value (defaults to the underlying DOM element's name)
12602      */
12603     hiddenName: undefined,
12604     /**
12605      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12606      */
12607     listClass: '',
12608     /**
12609      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12610      */
12611     selectedClass: 'active',
12612     
12613     /**
12614      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12615      */
12616     shadow:'sides',
12617     /**
12618      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12619      * anchor positions (defaults to 'tl-bl')
12620      */
12621     listAlign: 'tl-bl?',
12622     /**
12623      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12624      */
12625     maxHeight: 300,
12626     /**
12627      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12628      * query specified by the allQuery config option (defaults to 'query')
12629      */
12630     triggerAction: 'query',
12631     /**
12632      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12633      * (defaults to 4, does not apply if editable = false)
12634      */
12635     minChars : 4,
12636     /**
12637      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12638      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12639      */
12640     typeAhead: false,
12641     /**
12642      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12643      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12644      */
12645     queryDelay: 500,
12646     /**
12647      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12648      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12649      */
12650     pageSize: 0,
12651     /**
12652      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12653      * when editable = true (defaults to false)
12654      */
12655     selectOnFocus:false,
12656     /**
12657      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12658      */
12659     queryParam: 'query',
12660     /**
12661      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12662      * when mode = 'remote' (defaults to 'Loading...')
12663      */
12664     loadingText: 'Loading...',
12665     /**
12666      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12667      */
12668     resizable: false,
12669     /**
12670      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12671      */
12672     handleHeight : 8,
12673     /**
12674      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12675      * traditional select (defaults to true)
12676      */
12677     editable: true,
12678     /**
12679      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12680      */
12681     allQuery: '',
12682     /**
12683      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12684      */
12685     mode: 'remote',
12686     /**
12687      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12688      * listWidth has a higher value)
12689      */
12690     minListWidth : 70,
12691     /**
12692      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12693      * allow the user to set arbitrary text into the field (defaults to false)
12694      */
12695     forceSelection:false,
12696     /**
12697      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12698      * if typeAhead = true (defaults to 250)
12699      */
12700     typeAheadDelay : 250,
12701     /**
12702      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12703      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12704      */
12705     valueNotFoundText : undefined,
12706     /**
12707      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12708      */
12709     blockFocus : false,
12710     
12711     /**
12712      * @cfg {Boolean} disableClear Disable showing of clear button.
12713      */
12714     disableClear : false,
12715     /**
12716      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12717      */
12718     alwaysQuery : false,
12719     
12720     /**
12721      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12722      */
12723     multiple : false,
12724     
12725     /**
12726      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12727      */
12728     invalidClass : "has-warning",
12729     
12730     /**
12731      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12732      */
12733     validClass : "has-success",
12734     
12735     /**
12736      * @cfg {Boolean} specialFilter (true|false) special filter default false
12737      */
12738     specialFilter : false,
12739     
12740     /**
12741      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12742      */
12743     mobileTouchView : true,
12744     
12745     /**
12746      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12747      */
12748     useNativeIOS : false,
12749     
12750     ios_options : false,
12751     
12752     //private
12753     addicon : false,
12754     editicon: false,
12755     
12756     page: 0,
12757     hasQuery: false,
12758     append: false,
12759     loadNext: false,
12760     autoFocus : true,
12761     tickable : false,
12762     btnPosition : 'right',
12763     triggerList : true,
12764     showToggleBtn : true,
12765     animate : true,
12766     emptyResultText: 'Empty',
12767     triggerText : 'Select',
12768     emptyTitle : '',
12769     
12770     // element that contains real text value.. (when hidden is used..)
12771     
12772     getAutoCreate : function()
12773     {   
12774         var cfg = false;
12775         //render
12776         /*
12777          * Render classic select for iso
12778          */
12779         
12780         if(Roo.isIOS && this.useNativeIOS){
12781             cfg = this.getAutoCreateNativeIOS();
12782             return cfg;
12783         }
12784         
12785         /*
12786          * Touch Devices
12787          */
12788         
12789         if(Roo.isTouch && this.mobileTouchView){
12790             cfg = this.getAutoCreateTouchView();
12791             return cfg;;
12792         }
12793         
12794         /*
12795          *  Normal ComboBox
12796          */
12797         if(!this.tickable){
12798             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12799             return cfg;
12800         }
12801         
12802         /*
12803          *  ComboBox with tickable selections
12804          */
12805              
12806         var align = this.labelAlign || this.parentLabelAlign();
12807         
12808         cfg = {
12809             cls : 'form-group roo-combobox-tickable' //input-group
12810         };
12811         
12812         var btn_text_select = '';
12813         var btn_text_done = '';
12814         var btn_text_cancel = '';
12815         
12816         if (this.btn_text_show) {
12817             btn_text_select = 'Select';
12818             btn_text_done = 'Done';
12819             btn_text_cancel = 'Cancel'; 
12820         }
12821         
12822         var buttons = {
12823             tag : 'div',
12824             cls : 'tickable-buttons',
12825             cn : [
12826                 {
12827                     tag : 'button',
12828                     type : 'button',
12829                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12830                     //html : this.triggerText
12831                     html: btn_text_select
12832                 },
12833                 {
12834                     tag : 'button',
12835                     type : 'button',
12836                     name : 'ok',
12837                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12838                     //html : 'Done'
12839                     html: btn_text_done
12840                 },
12841                 {
12842                     tag : 'button',
12843                     type : 'button',
12844                     name : 'cancel',
12845                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12846                     //html : 'Cancel'
12847                     html: btn_text_cancel
12848                 }
12849             ]
12850         };
12851         
12852         if(this.editable){
12853             buttons.cn.unshift({
12854                 tag: 'input',
12855                 cls: 'roo-select2-search-field-input'
12856             });
12857         }
12858         
12859         var _this = this;
12860         
12861         Roo.each(buttons.cn, function(c){
12862             if (_this.size) {
12863                 c.cls += ' btn-' + _this.size;
12864             }
12865
12866             if (_this.disabled) {
12867                 c.disabled = true;
12868             }
12869         });
12870         
12871         var box = {
12872             tag: 'div',
12873             cn: [
12874                 {
12875                     tag: 'input',
12876                     type : 'hidden',
12877                     cls: 'form-hidden-field'
12878                 },
12879                 {
12880                     tag: 'ul',
12881                     cls: 'roo-select2-choices',
12882                     cn:[
12883                         {
12884                             tag: 'li',
12885                             cls: 'roo-select2-search-field',
12886                             cn: [
12887                                 buttons
12888                             ]
12889                         }
12890                     ]
12891                 }
12892             ]
12893         };
12894         
12895         var combobox = {
12896             cls: 'roo-select2-container input-group roo-select2-container-multi',
12897             cn: [
12898                 box
12899 //                {
12900 //                    tag: 'ul',
12901 //                    cls: 'typeahead typeahead-long dropdown-menu',
12902 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12903 //                }
12904             ]
12905         };
12906         
12907         if(this.hasFeedback && !this.allowBlank){
12908             
12909             var feedback = {
12910                 tag: 'span',
12911                 cls: 'glyphicon form-control-feedback'
12912             };
12913
12914             combobox.cn.push(feedback);
12915         }
12916         
12917         
12918         if (align ==='left' && this.fieldLabel.length) {
12919             
12920             cfg.cls += ' roo-form-group-label-left';
12921             
12922             cfg.cn = [
12923                 {
12924                     tag : 'i',
12925                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12926                     tooltip : 'This field is required'
12927                 },
12928                 {
12929                     tag: 'label',
12930                     'for' :  id,
12931                     cls : 'control-label',
12932                     html : this.fieldLabel
12933
12934                 },
12935                 {
12936                     cls : "", 
12937                     cn: [
12938                         combobox
12939                     ]
12940                 }
12941
12942             ];
12943             
12944             var labelCfg = cfg.cn[1];
12945             var contentCfg = cfg.cn[2];
12946             
12947
12948             if(this.indicatorpos == 'right'){
12949                 
12950                 cfg.cn = [
12951                     {
12952                         tag: 'label',
12953                         'for' :  id,
12954                         cls : 'control-label',
12955                         cn : [
12956                             {
12957                                 tag : 'span',
12958                                 html : this.fieldLabel
12959                             },
12960                             {
12961                                 tag : 'i',
12962                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12963                                 tooltip : 'This field is required'
12964                             }
12965                         ]
12966                     },
12967                     {
12968                         cls : "",
12969                         cn: [
12970                             combobox
12971                         ]
12972                     }
12973
12974                 ];
12975                 
12976                 
12977                 
12978                 labelCfg = cfg.cn[0];
12979                 contentCfg = cfg.cn[1];
12980             
12981             }
12982             
12983             if(this.labelWidth > 12){
12984                 labelCfg.style = "width: " + this.labelWidth + 'px';
12985             }
12986             
12987             if(this.labelWidth < 13 && this.labelmd == 0){
12988                 this.labelmd = this.labelWidth;
12989             }
12990             
12991             if(this.labellg > 0){
12992                 labelCfg.cls += ' col-lg-' + this.labellg;
12993                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12994             }
12995             
12996             if(this.labelmd > 0){
12997                 labelCfg.cls += ' col-md-' + this.labelmd;
12998                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12999             }
13000             
13001             if(this.labelsm > 0){
13002                 labelCfg.cls += ' col-sm-' + this.labelsm;
13003                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13004             }
13005             
13006             if(this.labelxs > 0){
13007                 labelCfg.cls += ' col-xs-' + this.labelxs;
13008                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13009             }
13010                 
13011                 
13012         } else if ( this.fieldLabel.length) {
13013 //                Roo.log(" label");
13014                  cfg.cn = [
13015                     {
13016                         tag : 'i',
13017                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13018                         tooltip : 'This field is required'
13019                     },
13020                     {
13021                         tag: 'label',
13022                         //cls : 'input-group-addon',
13023                         html : this.fieldLabel
13024                     },
13025                     combobox
13026                 ];
13027                 
13028                 if(this.indicatorpos == 'right'){
13029                     cfg.cn = [
13030                         {
13031                             tag: 'label',
13032                             //cls : 'input-group-addon',
13033                             html : this.fieldLabel
13034                         },
13035                         {
13036                             tag : 'i',
13037                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13038                             tooltip : 'This field is required'
13039                         },
13040                         combobox
13041                     ];
13042                     
13043                 }
13044
13045         } else {
13046             
13047 //                Roo.log(" no label && no align");
13048                 cfg = combobox
13049                      
13050                 
13051         }
13052          
13053         var settings=this;
13054         ['xs','sm','md','lg'].map(function(size){
13055             if (settings[size]) {
13056                 cfg.cls += ' col-' + size + '-' + settings[size];
13057             }
13058         });
13059         
13060         return cfg;
13061         
13062     },
13063     
13064     _initEventsCalled : false,
13065     
13066     // private
13067     initEvents: function()
13068     {   
13069         if (this._initEventsCalled) { // as we call render... prevent looping...
13070             return;
13071         }
13072         this._initEventsCalled = true;
13073         
13074         if (!this.store) {
13075             throw "can not find store for combo";
13076         }
13077         
13078         this.indicator = this.indicatorEl();
13079         
13080         this.store = Roo.factory(this.store, Roo.data);
13081         this.store.parent = this;
13082         
13083         // if we are building from html. then this element is so complex, that we can not really
13084         // use the rendered HTML.
13085         // so we have to trash and replace the previous code.
13086         if (Roo.XComponent.build_from_html) {
13087             // remove this element....
13088             var e = this.el.dom, k=0;
13089             while (e ) { e = e.previousSibling;  ++k;}
13090
13091             this.el.remove();
13092             
13093             this.el=false;
13094             this.rendered = false;
13095             
13096             this.render(this.parent().getChildContainer(true), k);
13097         }
13098         
13099         if(Roo.isIOS && this.useNativeIOS){
13100             this.initIOSView();
13101             return;
13102         }
13103         
13104         /*
13105          * Touch Devices
13106          */
13107         
13108         if(Roo.isTouch && this.mobileTouchView){
13109             this.initTouchView();
13110             return;
13111         }
13112         
13113         if(this.tickable){
13114             this.initTickableEvents();
13115             return;
13116         }
13117         
13118         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13119         
13120         if(this.hiddenName){
13121             
13122             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13123             
13124             this.hiddenField.dom.value =
13125                 this.hiddenValue !== undefined ? this.hiddenValue :
13126                 this.value !== undefined ? this.value : '';
13127
13128             // prevent input submission
13129             this.el.dom.removeAttribute('name');
13130             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13131              
13132              
13133         }
13134         //if(Roo.isGecko){
13135         //    this.el.dom.setAttribute('autocomplete', 'off');
13136         //}
13137         
13138         var cls = 'x-combo-list';
13139         
13140         //this.list = new Roo.Layer({
13141         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13142         //});
13143         
13144         var _this = this;
13145         
13146         (function(){
13147             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13148             _this.list.setWidth(lw);
13149         }).defer(100);
13150         
13151         this.list.on('mouseover', this.onViewOver, this);
13152         this.list.on('mousemove', this.onViewMove, this);
13153         this.list.on('scroll', this.onViewScroll, this);
13154         
13155         /*
13156         this.list.swallowEvent('mousewheel');
13157         this.assetHeight = 0;
13158
13159         if(this.title){
13160             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13161             this.assetHeight += this.header.getHeight();
13162         }
13163
13164         this.innerList = this.list.createChild({cls:cls+'-inner'});
13165         this.innerList.on('mouseover', this.onViewOver, this);
13166         this.innerList.on('mousemove', this.onViewMove, this);
13167         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13168         
13169         if(this.allowBlank && !this.pageSize && !this.disableClear){
13170             this.footer = this.list.createChild({cls:cls+'-ft'});
13171             this.pageTb = new Roo.Toolbar(this.footer);
13172            
13173         }
13174         if(this.pageSize){
13175             this.footer = this.list.createChild({cls:cls+'-ft'});
13176             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13177                     {pageSize: this.pageSize});
13178             
13179         }
13180         
13181         if (this.pageTb && this.allowBlank && !this.disableClear) {
13182             var _this = this;
13183             this.pageTb.add(new Roo.Toolbar.Fill(), {
13184                 cls: 'x-btn-icon x-btn-clear',
13185                 text: '&#160;',
13186                 handler: function()
13187                 {
13188                     _this.collapse();
13189                     _this.clearValue();
13190                     _this.onSelect(false, -1);
13191                 }
13192             });
13193         }
13194         if (this.footer) {
13195             this.assetHeight += this.footer.getHeight();
13196         }
13197         */
13198             
13199         if(!this.tpl){
13200             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13201         }
13202
13203         this.view = new Roo.View(this.list, this.tpl, {
13204             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13205         });
13206         //this.view.wrapEl.setDisplayed(false);
13207         this.view.on('click', this.onViewClick, this);
13208         
13209         
13210         this.store.on('beforeload', this.onBeforeLoad, this);
13211         this.store.on('load', this.onLoad, this);
13212         this.store.on('loadexception', this.onLoadException, this);
13213         /*
13214         if(this.resizable){
13215             this.resizer = new Roo.Resizable(this.list,  {
13216                pinned:true, handles:'se'
13217             });
13218             this.resizer.on('resize', function(r, w, h){
13219                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13220                 this.listWidth = w;
13221                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13222                 this.restrictHeight();
13223             }, this);
13224             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13225         }
13226         */
13227         if(!this.editable){
13228             this.editable = true;
13229             this.setEditable(false);
13230         }
13231         
13232         /*
13233         
13234         if (typeof(this.events.add.listeners) != 'undefined') {
13235             
13236             this.addicon = this.wrap.createChild(
13237                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13238        
13239             this.addicon.on('click', function(e) {
13240                 this.fireEvent('add', this);
13241             }, this);
13242         }
13243         if (typeof(this.events.edit.listeners) != 'undefined') {
13244             
13245             this.editicon = this.wrap.createChild(
13246                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13247             if (this.addicon) {
13248                 this.editicon.setStyle('margin-left', '40px');
13249             }
13250             this.editicon.on('click', function(e) {
13251                 
13252                 // we fire even  if inothing is selected..
13253                 this.fireEvent('edit', this, this.lastData );
13254                 
13255             }, this);
13256         }
13257         */
13258         
13259         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13260             "up" : function(e){
13261                 this.inKeyMode = true;
13262                 this.selectPrev();
13263             },
13264
13265             "down" : function(e){
13266                 if(!this.isExpanded()){
13267                     this.onTriggerClick();
13268                 }else{
13269                     this.inKeyMode = true;
13270                     this.selectNext();
13271                 }
13272             },
13273
13274             "enter" : function(e){
13275 //                this.onViewClick();
13276                 //return true;
13277                 this.collapse();
13278                 
13279                 if(this.fireEvent("specialkey", this, e)){
13280                     this.onViewClick(false);
13281                 }
13282                 
13283                 return true;
13284             },
13285
13286             "esc" : function(e){
13287                 this.collapse();
13288             },
13289
13290             "tab" : function(e){
13291                 this.collapse();
13292                 
13293                 if(this.fireEvent("specialkey", this, e)){
13294                     this.onViewClick(false);
13295                 }
13296                 
13297                 return true;
13298             },
13299
13300             scope : this,
13301
13302             doRelay : function(foo, bar, hname){
13303                 if(hname == 'down' || this.scope.isExpanded()){
13304                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13305                 }
13306                 return true;
13307             },
13308
13309             forceKeyDown: true
13310         });
13311         
13312         
13313         this.queryDelay = Math.max(this.queryDelay || 10,
13314                 this.mode == 'local' ? 10 : 250);
13315         
13316         
13317         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13318         
13319         if(this.typeAhead){
13320             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13321         }
13322         if(this.editable !== false){
13323             this.inputEl().on("keyup", this.onKeyUp, this);
13324         }
13325         if(this.forceSelection){
13326             this.inputEl().on('blur', this.doForce, this);
13327         }
13328         
13329         if(this.multiple){
13330             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13331             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13332         }
13333     },
13334     
13335     initTickableEvents: function()
13336     {   
13337         this.createList();
13338         
13339         if(this.hiddenName){
13340             
13341             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13342             
13343             this.hiddenField.dom.value =
13344                 this.hiddenValue !== undefined ? this.hiddenValue :
13345                 this.value !== undefined ? this.value : '';
13346
13347             // prevent input submission
13348             this.el.dom.removeAttribute('name');
13349             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13350              
13351              
13352         }
13353         
13354 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13355         
13356         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13357         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13358         if(this.triggerList){
13359             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13360         }
13361          
13362         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13363         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13364         
13365         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13366         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13367         
13368         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13369         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13370         
13371         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13372         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13373         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13374         
13375         this.okBtn.hide();
13376         this.cancelBtn.hide();
13377         
13378         var _this = this;
13379         
13380         (function(){
13381             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13382             _this.list.setWidth(lw);
13383         }).defer(100);
13384         
13385         this.list.on('mouseover', this.onViewOver, this);
13386         this.list.on('mousemove', this.onViewMove, this);
13387         
13388         this.list.on('scroll', this.onViewScroll, this);
13389         
13390         if(!this.tpl){
13391             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>';
13392         }
13393
13394         this.view = new Roo.View(this.list, this.tpl, {
13395             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13396         });
13397         
13398         //this.view.wrapEl.setDisplayed(false);
13399         this.view.on('click', this.onViewClick, this);
13400         
13401         
13402         
13403         this.store.on('beforeload', this.onBeforeLoad, this);
13404         this.store.on('load', this.onLoad, this);
13405         this.store.on('loadexception', this.onLoadException, this);
13406         
13407         if(this.editable){
13408             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13409                 "up" : function(e){
13410                     this.inKeyMode = true;
13411                     this.selectPrev();
13412                 },
13413
13414                 "down" : function(e){
13415                     this.inKeyMode = true;
13416                     this.selectNext();
13417                 },
13418
13419                 "enter" : function(e){
13420                     if(this.fireEvent("specialkey", this, e)){
13421                         this.onViewClick(false);
13422                     }
13423                     
13424                     return true;
13425                 },
13426
13427                 "esc" : function(e){
13428                     this.onTickableFooterButtonClick(e, false, false);
13429                 },
13430
13431                 "tab" : function(e){
13432                     this.fireEvent("specialkey", this, e);
13433                     
13434                     this.onTickableFooterButtonClick(e, false, false);
13435                     
13436                     return true;
13437                 },
13438
13439                 scope : this,
13440
13441                 doRelay : function(e, fn, key){
13442                     if(this.scope.isExpanded()){
13443                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13444                     }
13445                     return true;
13446                 },
13447
13448                 forceKeyDown: true
13449             });
13450         }
13451         
13452         this.queryDelay = Math.max(this.queryDelay || 10,
13453                 this.mode == 'local' ? 10 : 250);
13454         
13455         
13456         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13457         
13458         if(this.typeAhead){
13459             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13460         }
13461         
13462         if(this.editable !== false){
13463             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13464         }
13465         
13466         this.indicator = this.indicatorEl();
13467         
13468         if(this.indicator){
13469             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13470             this.indicator.hide();
13471         }
13472         
13473     },
13474
13475     onDestroy : function(){
13476         if(this.view){
13477             this.view.setStore(null);
13478             this.view.el.removeAllListeners();
13479             this.view.el.remove();
13480             this.view.purgeListeners();
13481         }
13482         if(this.list){
13483             this.list.dom.innerHTML  = '';
13484         }
13485         
13486         if(this.store){
13487             this.store.un('beforeload', this.onBeforeLoad, this);
13488             this.store.un('load', this.onLoad, this);
13489             this.store.un('loadexception', this.onLoadException, this);
13490         }
13491         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13492     },
13493
13494     // private
13495     fireKey : function(e){
13496         if(e.isNavKeyPress() && !this.list.isVisible()){
13497             this.fireEvent("specialkey", this, e);
13498         }
13499     },
13500
13501     // private
13502     onResize: function(w, h){
13503 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13504 //        
13505 //        if(typeof w != 'number'){
13506 //            // we do not handle it!?!?
13507 //            return;
13508 //        }
13509 //        var tw = this.trigger.getWidth();
13510 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13511 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13512 //        var x = w - tw;
13513 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13514 //            
13515 //        //this.trigger.setStyle('left', x+'px');
13516 //        
13517 //        if(this.list && this.listWidth === undefined){
13518 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13519 //            this.list.setWidth(lw);
13520 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13521 //        }
13522         
13523     
13524         
13525     },
13526
13527     /**
13528      * Allow or prevent the user from directly editing the field text.  If false is passed,
13529      * the user will only be able to select from the items defined in the dropdown list.  This method
13530      * is the runtime equivalent of setting the 'editable' config option at config time.
13531      * @param {Boolean} value True to allow the user to directly edit the field text
13532      */
13533     setEditable : function(value){
13534         if(value == this.editable){
13535             return;
13536         }
13537         this.editable = value;
13538         if(!value){
13539             this.inputEl().dom.setAttribute('readOnly', true);
13540             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13541             this.inputEl().addClass('x-combo-noedit');
13542         }else{
13543             this.inputEl().dom.setAttribute('readOnly', false);
13544             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13545             this.inputEl().removeClass('x-combo-noedit');
13546         }
13547     },
13548
13549     // private
13550     
13551     onBeforeLoad : function(combo,opts){
13552         if(!this.hasFocus){
13553             return;
13554         }
13555          if (!opts.add) {
13556             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13557          }
13558         this.restrictHeight();
13559         this.selectedIndex = -1;
13560     },
13561
13562     // private
13563     onLoad : function(){
13564         
13565         this.hasQuery = false;
13566         
13567         if(!this.hasFocus){
13568             return;
13569         }
13570         
13571         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13572             this.loading.hide();
13573         }
13574         
13575         if(this.store.getCount() > 0){
13576             
13577             this.expand();
13578             this.restrictHeight();
13579             if(this.lastQuery == this.allQuery){
13580                 if(this.editable && !this.tickable){
13581                     this.inputEl().dom.select();
13582                 }
13583                 
13584                 if(
13585                     !this.selectByValue(this.value, true) &&
13586                     this.autoFocus && 
13587                     (
13588                         !this.store.lastOptions ||
13589                         typeof(this.store.lastOptions.add) == 'undefined' || 
13590                         this.store.lastOptions.add != true
13591                     )
13592                 ){
13593                     this.select(0, true);
13594                 }
13595             }else{
13596                 if(this.autoFocus){
13597                     this.selectNext();
13598                 }
13599                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13600                     this.taTask.delay(this.typeAheadDelay);
13601                 }
13602             }
13603         }else{
13604             this.onEmptyResults();
13605         }
13606         
13607         //this.el.focus();
13608     },
13609     // private
13610     onLoadException : function()
13611     {
13612         this.hasQuery = false;
13613         
13614         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13615             this.loading.hide();
13616         }
13617         
13618         if(this.tickable && this.editable){
13619             return;
13620         }
13621         
13622         this.collapse();
13623         // only causes errors at present
13624         //Roo.log(this.store.reader.jsonData);
13625         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13626             // fixme
13627             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13628         //}
13629         
13630         
13631     },
13632     // private
13633     onTypeAhead : function(){
13634         if(this.store.getCount() > 0){
13635             var r = this.store.getAt(0);
13636             var newValue = r.data[this.displayField];
13637             var len = newValue.length;
13638             var selStart = this.getRawValue().length;
13639             
13640             if(selStart != len){
13641                 this.setRawValue(newValue);
13642                 this.selectText(selStart, newValue.length);
13643             }
13644         }
13645     },
13646
13647     // private
13648     onSelect : function(record, index){
13649         
13650         if(this.fireEvent('beforeselect', this, record, index) !== false){
13651         
13652             this.setFromData(index > -1 ? record.data : false);
13653             
13654             this.collapse();
13655             this.fireEvent('select', this, record, index);
13656         }
13657     },
13658
13659     /**
13660      * Returns the currently selected field value or empty string if no value is set.
13661      * @return {String} value The selected value
13662      */
13663     getValue : function()
13664     {
13665         if(Roo.isIOS && this.useNativeIOS){
13666             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13667         }
13668         
13669         if(this.multiple){
13670             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13671         }
13672         
13673         if(this.valueField){
13674             return typeof this.value != 'undefined' ? this.value : '';
13675         }else{
13676             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13677         }
13678     },
13679     
13680     getRawValue : function()
13681     {
13682         if(Roo.isIOS && this.useNativeIOS){
13683             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13684         }
13685         
13686         var v = this.inputEl().getValue();
13687         
13688         return v;
13689     },
13690
13691     /**
13692      * Clears any text/value currently set in the field
13693      */
13694     clearValue : function(){
13695         
13696         if(this.hiddenField){
13697             this.hiddenField.dom.value = '';
13698         }
13699         this.value = '';
13700         this.setRawValue('');
13701         this.lastSelectionText = '';
13702         this.lastData = false;
13703         
13704         var close = this.closeTriggerEl();
13705         
13706         if(close){
13707             close.hide();
13708         }
13709         
13710         this.validate();
13711         
13712     },
13713
13714     /**
13715      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13716      * will be displayed in the field.  If the value does not match the data value of an existing item,
13717      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13718      * Otherwise the field will be blank (although the value will still be set).
13719      * @param {String} value The value to match
13720      */
13721     setValue : function(v)
13722     {
13723         if(Roo.isIOS && this.useNativeIOS){
13724             this.setIOSValue(v);
13725             return;
13726         }
13727         
13728         if(this.multiple){
13729             this.syncValue();
13730             return;
13731         }
13732         
13733         var text = v;
13734         if(this.valueField){
13735             var r = this.findRecord(this.valueField, v);
13736             if(r){
13737                 text = r.data[this.displayField];
13738             }else if(this.valueNotFoundText !== undefined){
13739                 text = this.valueNotFoundText;
13740             }
13741         }
13742         this.lastSelectionText = text;
13743         if(this.hiddenField){
13744             this.hiddenField.dom.value = v;
13745         }
13746         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13747         this.value = v;
13748         
13749         var close = this.closeTriggerEl();
13750         
13751         if(close){
13752             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13753         }
13754         
13755         this.validate();
13756     },
13757     /**
13758      * @property {Object} the last set data for the element
13759      */
13760     
13761     lastData : false,
13762     /**
13763      * Sets the value of the field based on a object which is related to the record format for the store.
13764      * @param {Object} value the value to set as. or false on reset?
13765      */
13766     setFromData : function(o){
13767         
13768         if(this.multiple){
13769             this.addItem(o);
13770             return;
13771         }
13772             
13773         var dv = ''; // display value
13774         var vv = ''; // value value..
13775         this.lastData = o;
13776         if (this.displayField) {
13777             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13778         } else {
13779             // this is an error condition!!!
13780             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13781         }
13782         
13783         if(this.valueField){
13784             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13785         }
13786         
13787         var close = this.closeTriggerEl();
13788         
13789         if(close){
13790             if(dv.length || vv * 1 > 0){
13791                 close.show() ;
13792                 this.blockFocus=true;
13793             } else {
13794                 close.hide();
13795             }             
13796         }
13797         
13798         if(this.hiddenField){
13799             this.hiddenField.dom.value = vv;
13800             
13801             this.lastSelectionText = dv;
13802             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13803             this.value = vv;
13804             return;
13805         }
13806         // no hidden field.. - we store the value in 'value', but still display
13807         // display field!!!!
13808         this.lastSelectionText = dv;
13809         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13810         this.value = vv;
13811         
13812         
13813         
13814     },
13815     // private
13816     reset : function(){
13817         // overridden so that last data is reset..
13818         
13819         if(this.multiple){
13820             this.clearItem();
13821             return;
13822         }
13823         
13824         this.setValue(this.originalValue);
13825         //this.clearInvalid();
13826         this.lastData = false;
13827         if (this.view) {
13828             this.view.clearSelections();
13829         }
13830         
13831         this.validate();
13832     },
13833     // private
13834     findRecord : function(prop, value){
13835         var record;
13836         if(this.store.getCount() > 0){
13837             this.store.each(function(r){
13838                 if(r.data[prop] == value){
13839                     record = r;
13840                     return false;
13841                 }
13842                 return true;
13843             });
13844         }
13845         return record;
13846     },
13847     
13848     getName: function()
13849     {
13850         // returns hidden if it's set..
13851         if (!this.rendered) {return ''};
13852         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13853         
13854     },
13855     // private
13856     onViewMove : function(e, t){
13857         this.inKeyMode = false;
13858     },
13859
13860     // private
13861     onViewOver : function(e, t){
13862         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13863             return;
13864         }
13865         var item = this.view.findItemFromChild(t);
13866         
13867         if(item){
13868             var index = this.view.indexOf(item);
13869             this.select(index, false);
13870         }
13871     },
13872
13873     // private
13874     onViewClick : function(view, doFocus, el, e)
13875     {
13876         var index = this.view.getSelectedIndexes()[0];
13877         
13878         var r = this.store.getAt(index);
13879         
13880         if(this.tickable){
13881             
13882             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13883                 return;
13884             }
13885             
13886             var rm = false;
13887             var _this = this;
13888             
13889             Roo.each(this.tickItems, function(v,k){
13890                 
13891                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13892                     Roo.log(v);
13893                     _this.tickItems.splice(k, 1);
13894                     
13895                     if(typeof(e) == 'undefined' && view == false){
13896                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13897                     }
13898                     
13899                     rm = true;
13900                     return;
13901                 }
13902             });
13903             
13904             if(rm){
13905                 return;
13906             }
13907             
13908             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13909                 this.tickItems.push(r.data);
13910             }
13911             
13912             if(typeof(e) == 'undefined' && view == false){
13913                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13914             }
13915                     
13916             return;
13917         }
13918         
13919         if(r){
13920             this.onSelect(r, index);
13921         }
13922         if(doFocus !== false && !this.blockFocus){
13923             this.inputEl().focus();
13924         }
13925     },
13926
13927     // private
13928     restrictHeight : function(){
13929         //this.innerList.dom.style.height = '';
13930         //var inner = this.innerList.dom;
13931         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13932         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13933         //this.list.beginUpdate();
13934         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13935         this.list.alignTo(this.inputEl(), this.listAlign);
13936         this.list.alignTo(this.inputEl(), this.listAlign);
13937         //this.list.endUpdate();
13938     },
13939
13940     // private
13941     onEmptyResults : function(){
13942         
13943         if(this.tickable && this.editable){
13944             this.restrictHeight();
13945             return;
13946         }
13947         
13948         this.collapse();
13949     },
13950
13951     /**
13952      * Returns true if the dropdown list is expanded, else false.
13953      */
13954     isExpanded : function(){
13955         return this.list.isVisible();
13956     },
13957
13958     /**
13959      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13960      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13961      * @param {String} value The data value of the item to select
13962      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13963      * selected item if it is not currently in view (defaults to true)
13964      * @return {Boolean} True if the value matched an item in the list, else false
13965      */
13966     selectByValue : function(v, scrollIntoView){
13967         if(v !== undefined && v !== null){
13968             var r = this.findRecord(this.valueField || this.displayField, v);
13969             if(r){
13970                 this.select(this.store.indexOf(r), scrollIntoView);
13971                 return true;
13972             }
13973         }
13974         return false;
13975     },
13976
13977     /**
13978      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13979      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13980      * @param {Number} index The zero-based index of the list item to select
13981      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13982      * selected item if it is not currently in view (defaults to true)
13983      */
13984     select : function(index, scrollIntoView){
13985         this.selectedIndex = index;
13986         this.view.select(index);
13987         if(scrollIntoView !== false){
13988             var el = this.view.getNode(index);
13989             /*
13990              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13991              */
13992             if(el){
13993                 this.list.scrollChildIntoView(el, false);
13994             }
13995         }
13996     },
13997
13998     // private
13999     selectNext : function(){
14000         var ct = this.store.getCount();
14001         if(ct > 0){
14002             if(this.selectedIndex == -1){
14003                 this.select(0);
14004             }else if(this.selectedIndex < ct-1){
14005                 this.select(this.selectedIndex+1);
14006             }
14007         }
14008     },
14009
14010     // private
14011     selectPrev : function(){
14012         var ct = this.store.getCount();
14013         if(ct > 0){
14014             if(this.selectedIndex == -1){
14015                 this.select(0);
14016             }else if(this.selectedIndex != 0){
14017                 this.select(this.selectedIndex-1);
14018             }
14019         }
14020     },
14021
14022     // private
14023     onKeyUp : function(e){
14024         if(this.editable !== false && !e.isSpecialKey()){
14025             this.lastKey = e.getKey();
14026             this.dqTask.delay(this.queryDelay);
14027         }
14028     },
14029
14030     // private
14031     validateBlur : function(){
14032         return !this.list || !this.list.isVisible();   
14033     },
14034
14035     // private
14036     initQuery : function(){
14037         
14038         var v = this.getRawValue();
14039         
14040         if(this.tickable && this.editable){
14041             v = this.tickableInputEl().getValue();
14042         }
14043         
14044         this.doQuery(v);
14045     },
14046
14047     // private
14048     doForce : function(){
14049         if(this.inputEl().dom.value.length > 0){
14050             this.inputEl().dom.value =
14051                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14052              
14053         }
14054     },
14055
14056     /**
14057      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14058      * query allowing the query action to be canceled if needed.
14059      * @param {String} query The SQL query to execute
14060      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14061      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14062      * saved in the current store (defaults to false)
14063      */
14064     doQuery : function(q, forceAll){
14065         
14066         if(q === undefined || q === null){
14067             q = '';
14068         }
14069         var qe = {
14070             query: q,
14071             forceAll: forceAll,
14072             combo: this,
14073             cancel:false
14074         };
14075         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14076             return false;
14077         }
14078         q = qe.query;
14079         
14080         forceAll = qe.forceAll;
14081         if(forceAll === true || (q.length >= this.minChars)){
14082             
14083             this.hasQuery = true;
14084             
14085             if(this.lastQuery != q || this.alwaysQuery){
14086                 this.lastQuery = q;
14087                 if(this.mode == 'local'){
14088                     this.selectedIndex = -1;
14089                     if(forceAll){
14090                         this.store.clearFilter();
14091                     }else{
14092                         
14093                         if(this.specialFilter){
14094                             this.fireEvent('specialfilter', this);
14095                             this.onLoad();
14096                             return;
14097                         }
14098                         
14099                         this.store.filter(this.displayField, q);
14100                     }
14101                     
14102                     this.store.fireEvent("datachanged", this.store);
14103                     
14104                     this.onLoad();
14105                     
14106                     
14107                 }else{
14108                     
14109                     this.store.baseParams[this.queryParam] = q;
14110                     
14111                     var options = {params : this.getParams(q)};
14112                     
14113                     if(this.loadNext){
14114                         options.add = true;
14115                         options.params.start = this.page * this.pageSize;
14116                     }
14117                     
14118                     this.store.load(options);
14119                     
14120                     /*
14121                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14122                      *  we should expand the list on onLoad
14123                      *  so command out it
14124                      */
14125 //                    this.expand();
14126                 }
14127             }else{
14128                 this.selectedIndex = -1;
14129                 this.onLoad();   
14130             }
14131         }
14132         
14133         this.loadNext = false;
14134     },
14135     
14136     // private
14137     getParams : function(q){
14138         var p = {};
14139         //p[this.queryParam] = q;
14140         
14141         if(this.pageSize){
14142             p.start = 0;
14143             p.limit = this.pageSize;
14144         }
14145         return p;
14146     },
14147
14148     /**
14149      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14150      */
14151     collapse : function(){
14152         if(!this.isExpanded()){
14153             return;
14154         }
14155         
14156         this.list.hide();
14157         
14158         this.hasFocus = false;
14159         
14160         if(this.tickable){
14161             this.okBtn.hide();
14162             this.cancelBtn.hide();
14163             this.trigger.show();
14164             
14165             if(this.editable){
14166                 this.tickableInputEl().dom.value = '';
14167                 this.tickableInputEl().blur();
14168             }
14169             
14170         }
14171         
14172         Roo.get(document).un('mousedown', this.collapseIf, this);
14173         Roo.get(document).un('mousewheel', this.collapseIf, this);
14174         if (!this.editable) {
14175             Roo.get(document).un('keydown', this.listKeyPress, this);
14176         }
14177         this.fireEvent('collapse', this);
14178         
14179         this.validate();
14180     },
14181
14182     // private
14183     collapseIf : function(e){
14184         var in_combo  = e.within(this.el);
14185         var in_list =  e.within(this.list);
14186         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14187         
14188         if (in_combo || in_list || is_list) {
14189             //e.stopPropagation();
14190             return;
14191         }
14192         
14193         if(this.tickable){
14194             this.onTickableFooterButtonClick(e, false, false);
14195         }
14196
14197         this.collapse();
14198         
14199     },
14200
14201     /**
14202      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14203      */
14204     expand : function(){
14205        
14206         if(this.isExpanded() || !this.hasFocus){
14207             return;
14208         }
14209         
14210         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14211         this.list.setWidth(lw);
14212         
14213         Roo.log('expand');
14214         
14215         this.list.show();
14216         
14217         this.restrictHeight();
14218         
14219         if(this.tickable){
14220             
14221             this.tickItems = Roo.apply([], this.item);
14222             
14223             this.okBtn.show();
14224             this.cancelBtn.show();
14225             this.trigger.hide();
14226             
14227             if(this.editable){
14228                 this.tickableInputEl().focus();
14229             }
14230             
14231         }
14232         
14233         Roo.get(document).on('mousedown', this.collapseIf, this);
14234         Roo.get(document).on('mousewheel', this.collapseIf, this);
14235         if (!this.editable) {
14236             Roo.get(document).on('keydown', this.listKeyPress, this);
14237         }
14238         
14239         this.fireEvent('expand', this);
14240     },
14241
14242     // private
14243     // Implements the default empty TriggerField.onTriggerClick function
14244     onTriggerClick : function(e)
14245     {
14246         Roo.log('trigger click');
14247         
14248         if(this.disabled || !this.triggerList){
14249             return;
14250         }
14251         
14252         this.page = 0;
14253         this.loadNext = false;
14254         
14255         if(this.isExpanded()){
14256             this.collapse();
14257             if (!this.blockFocus) {
14258                 this.inputEl().focus();
14259             }
14260             
14261         }else {
14262             this.hasFocus = true;
14263             if(this.triggerAction == 'all') {
14264                 this.doQuery(this.allQuery, true);
14265             } else {
14266                 this.doQuery(this.getRawValue());
14267             }
14268             if (!this.blockFocus) {
14269                 this.inputEl().focus();
14270             }
14271         }
14272     },
14273     
14274     onTickableTriggerClick : function(e)
14275     {
14276         if(this.disabled){
14277             return;
14278         }
14279         
14280         this.page = 0;
14281         this.loadNext = false;
14282         this.hasFocus = true;
14283         
14284         if(this.triggerAction == 'all') {
14285             this.doQuery(this.allQuery, true);
14286         } else {
14287             this.doQuery(this.getRawValue());
14288         }
14289     },
14290     
14291     onSearchFieldClick : function(e)
14292     {
14293         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14294             this.onTickableFooterButtonClick(e, false, false);
14295             return;
14296         }
14297         
14298         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14299             return;
14300         }
14301         
14302         this.page = 0;
14303         this.loadNext = false;
14304         this.hasFocus = true;
14305         
14306         if(this.triggerAction == 'all') {
14307             this.doQuery(this.allQuery, true);
14308         } else {
14309             this.doQuery(this.getRawValue());
14310         }
14311     },
14312     
14313     listKeyPress : function(e)
14314     {
14315         //Roo.log('listkeypress');
14316         // scroll to first matching element based on key pres..
14317         if (e.isSpecialKey()) {
14318             return false;
14319         }
14320         var k = String.fromCharCode(e.getKey()).toUpperCase();
14321         //Roo.log(k);
14322         var match  = false;
14323         var csel = this.view.getSelectedNodes();
14324         var cselitem = false;
14325         if (csel.length) {
14326             var ix = this.view.indexOf(csel[0]);
14327             cselitem  = this.store.getAt(ix);
14328             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14329                 cselitem = false;
14330             }
14331             
14332         }
14333         
14334         this.store.each(function(v) { 
14335             if (cselitem) {
14336                 // start at existing selection.
14337                 if (cselitem.id == v.id) {
14338                     cselitem = false;
14339                 }
14340                 return true;
14341             }
14342                 
14343             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14344                 match = this.store.indexOf(v);
14345                 return false;
14346             }
14347             return true;
14348         }, this);
14349         
14350         if (match === false) {
14351             return true; // no more action?
14352         }
14353         // scroll to?
14354         this.view.select(match);
14355         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14356         sn.scrollIntoView(sn.dom.parentNode, false);
14357     },
14358     
14359     onViewScroll : function(e, t){
14360         
14361         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){
14362             return;
14363         }
14364         
14365         this.hasQuery = true;
14366         
14367         this.loading = this.list.select('.loading', true).first();
14368         
14369         if(this.loading === null){
14370             this.list.createChild({
14371                 tag: 'div',
14372                 cls: 'loading roo-select2-more-results roo-select2-active',
14373                 html: 'Loading more results...'
14374             });
14375             
14376             this.loading = this.list.select('.loading', true).first();
14377             
14378             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14379             
14380             this.loading.hide();
14381         }
14382         
14383         this.loading.show();
14384         
14385         var _combo = this;
14386         
14387         this.page++;
14388         this.loadNext = true;
14389         
14390         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14391         
14392         return;
14393     },
14394     
14395     addItem : function(o)
14396     {   
14397         var dv = ''; // display value
14398         
14399         if (this.displayField) {
14400             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14401         } else {
14402             // this is an error condition!!!
14403             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14404         }
14405         
14406         if(!dv.length){
14407             return;
14408         }
14409         
14410         var choice = this.choices.createChild({
14411             tag: 'li',
14412             cls: 'roo-select2-search-choice',
14413             cn: [
14414                 {
14415                     tag: 'div',
14416                     html: dv
14417                 },
14418                 {
14419                     tag: 'a',
14420                     href: '#',
14421                     cls: 'roo-select2-search-choice-close fa fa-times',
14422                     tabindex: '-1'
14423                 }
14424             ]
14425             
14426         }, this.searchField);
14427         
14428         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14429         
14430         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14431         
14432         this.item.push(o);
14433         
14434         this.lastData = o;
14435         
14436         this.syncValue();
14437         
14438         this.inputEl().dom.value = '';
14439         
14440         this.validate();
14441     },
14442     
14443     onRemoveItem : function(e, _self, o)
14444     {
14445         e.preventDefault();
14446         
14447         this.lastItem = Roo.apply([], this.item);
14448         
14449         var index = this.item.indexOf(o.data) * 1;
14450         
14451         if( index < 0){
14452             Roo.log('not this item?!');
14453             return;
14454         }
14455         
14456         this.item.splice(index, 1);
14457         o.item.remove();
14458         
14459         this.syncValue();
14460         
14461         this.fireEvent('remove', this, e);
14462         
14463         this.validate();
14464         
14465     },
14466     
14467     syncValue : function()
14468     {
14469         if(!this.item.length){
14470             this.clearValue();
14471             return;
14472         }
14473             
14474         var value = [];
14475         var _this = this;
14476         Roo.each(this.item, function(i){
14477             if(_this.valueField){
14478                 value.push(i[_this.valueField]);
14479                 return;
14480             }
14481
14482             value.push(i);
14483         });
14484
14485         this.value = value.join(',');
14486
14487         if(this.hiddenField){
14488             this.hiddenField.dom.value = this.value;
14489         }
14490         
14491         this.store.fireEvent("datachanged", this.store);
14492         
14493         this.validate();
14494     },
14495     
14496     clearItem : function()
14497     {
14498         if(!this.multiple){
14499             return;
14500         }
14501         
14502         this.item = [];
14503         
14504         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14505            c.remove();
14506         });
14507         
14508         this.syncValue();
14509         
14510         this.validate();
14511         
14512         if(this.tickable && !Roo.isTouch){
14513             this.view.refresh();
14514         }
14515     },
14516     
14517     inputEl: function ()
14518     {
14519         if(Roo.isIOS && this.useNativeIOS){
14520             return this.el.select('select.roo-ios-select', true).first();
14521         }
14522         
14523         if(Roo.isTouch && this.mobileTouchView){
14524             return this.el.select('input.form-control',true).first();
14525         }
14526         
14527         if(this.tickable){
14528             return this.searchField;
14529         }
14530         
14531         return this.el.select('input.form-control',true).first();
14532     },
14533     
14534     onTickableFooterButtonClick : function(e, btn, el)
14535     {
14536         e.preventDefault();
14537         
14538         this.lastItem = Roo.apply([], this.item);
14539         
14540         if(btn && btn.name == 'cancel'){
14541             this.tickItems = Roo.apply([], this.item);
14542             this.collapse();
14543             return;
14544         }
14545         
14546         this.clearItem();
14547         
14548         var _this = this;
14549         
14550         Roo.each(this.tickItems, function(o){
14551             _this.addItem(o);
14552         });
14553         
14554         this.collapse();
14555         
14556     },
14557     
14558     validate : function()
14559     {
14560         var v = this.getRawValue();
14561         
14562         if(this.multiple){
14563             v = this.getValue();
14564         }
14565         
14566         if(this.disabled || this.allowBlank || v.length){
14567             this.markValid();
14568             return true;
14569         }
14570         
14571         this.markInvalid();
14572         return false;
14573     },
14574     
14575     tickableInputEl : function()
14576     {
14577         if(!this.tickable || !this.editable){
14578             return this.inputEl();
14579         }
14580         
14581         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14582     },
14583     
14584     
14585     getAutoCreateTouchView : function()
14586     {
14587         var id = Roo.id();
14588         
14589         var cfg = {
14590             cls: 'form-group' //input-group
14591         };
14592         
14593         var input =  {
14594             tag: 'input',
14595             id : id,
14596             type : this.inputType,
14597             cls : 'form-control x-combo-noedit',
14598             autocomplete: 'new-password',
14599             placeholder : this.placeholder || '',
14600             readonly : true
14601         };
14602         
14603         if (this.name) {
14604             input.name = this.name;
14605         }
14606         
14607         if (this.size) {
14608             input.cls += ' input-' + this.size;
14609         }
14610         
14611         if (this.disabled) {
14612             input.disabled = true;
14613         }
14614         
14615         var inputblock = {
14616             cls : '',
14617             cn : [
14618                 input
14619             ]
14620         };
14621         
14622         if(this.before){
14623             inputblock.cls += ' input-group';
14624             
14625             inputblock.cn.unshift({
14626                 tag :'span',
14627                 cls : 'input-group-addon',
14628                 html : this.before
14629             });
14630         }
14631         
14632         if(this.removable && !this.multiple){
14633             inputblock.cls += ' roo-removable';
14634             
14635             inputblock.cn.push({
14636                 tag: 'button',
14637                 html : 'x',
14638                 cls : 'roo-combo-removable-btn close'
14639             });
14640         }
14641
14642         if(this.hasFeedback && !this.allowBlank){
14643             
14644             inputblock.cls += ' has-feedback';
14645             
14646             inputblock.cn.push({
14647                 tag: 'span',
14648                 cls: 'glyphicon form-control-feedback'
14649             });
14650             
14651         }
14652         
14653         if (this.after) {
14654             
14655             inputblock.cls += (this.before) ? '' : ' input-group';
14656             
14657             inputblock.cn.push({
14658                 tag :'span',
14659                 cls : 'input-group-addon',
14660                 html : this.after
14661             });
14662         }
14663
14664         var box = {
14665             tag: 'div',
14666             cn: [
14667                 {
14668                     tag: 'input',
14669                     type : 'hidden',
14670                     cls: 'form-hidden-field'
14671                 },
14672                 inputblock
14673             ]
14674             
14675         };
14676         
14677         if(this.multiple){
14678             box = {
14679                 tag: 'div',
14680                 cn: [
14681                     {
14682                         tag: 'input',
14683                         type : 'hidden',
14684                         cls: 'form-hidden-field'
14685                     },
14686                     {
14687                         tag: 'ul',
14688                         cls: 'roo-select2-choices',
14689                         cn:[
14690                             {
14691                                 tag: 'li',
14692                                 cls: 'roo-select2-search-field',
14693                                 cn: [
14694
14695                                     inputblock
14696                                 ]
14697                             }
14698                         ]
14699                     }
14700                 ]
14701             }
14702         };
14703         
14704         var combobox = {
14705             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14706             cn: [
14707                 box
14708             ]
14709         };
14710         
14711         if(!this.multiple && this.showToggleBtn){
14712             
14713             var caret = {
14714                         tag: 'span',
14715                         cls: 'caret'
14716             };
14717             
14718             if (this.caret != false) {
14719                 caret = {
14720                      tag: 'i',
14721                      cls: 'fa fa-' + this.caret
14722                 };
14723                 
14724             }
14725             
14726             combobox.cn.push({
14727                 tag :'span',
14728                 cls : 'input-group-addon btn dropdown-toggle',
14729                 cn : [
14730                     caret,
14731                     {
14732                         tag: 'span',
14733                         cls: 'combobox-clear',
14734                         cn  : [
14735                             {
14736                                 tag : 'i',
14737                                 cls: 'icon-remove'
14738                             }
14739                         ]
14740                     }
14741                 ]
14742
14743             })
14744         }
14745         
14746         if(this.multiple){
14747             combobox.cls += ' roo-select2-container-multi';
14748         }
14749         
14750         var align = this.labelAlign || this.parentLabelAlign();
14751         
14752         if (align ==='left' && this.fieldLabel.length) {
14753
14754             cfg.cn = [
14755                 {
14756                    tag : 'i',
14757                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14758                    tooltip : 'This field is required'
14759                 },
14760                 {
14761                     tag: 'label',
14762                     cls : 'control-label',
14763                     html : this.fieldLabel
14764
14765                 },
14766                 {
14767                     cls : '', 
14768                     cn: [
14769                         combobox
14770                     ]
14771                 }
14772             ];
14773             
14774             var labelCfg = cfg.cn[1];
14775             var contentCfg = cfg.cn[2];
14776             
14777
14778             if(this.indicatorpos == 'right'){
14779                 cfg.cn = [
14780                     {
14781                         tag: 'label',
14782                         'for' :  id,
14783                         cls : 'control-label',
14784                         cn : [
14785                             {
14786                                 tag : 'span',
14787                                 html : this.fieldLabel
14788                             },
14789                             {
14790                                 tag : 'i',
14791                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14792                                 tooltip : 'This field is required'
14793                             }
14794                         ]
14795                     },
14796                     {
14797                         cls : "",
14798                         cn: [
14799                             combobox
14800                         ]
14801                     }
14802
14803                 ];
14804                 
14805                 labelCfg = cfg.cn[0];
14806                 contentCfg = cfg.cn[1];
14807             }
14808             
14809            
14810             
14811             if(this.labelWidth > 12){
14812                 labelCfg.style = "width: " + this.labelWidth + 'px';
14813             }
14814             
14815             if(this.labelWidth < 13 && this.labelmd == 0){
14816                 this.labelmd = this.labelWidth;
14817             }
14818             
14819             if(this.labellg > 0){
14820                 labelCfg.cls += ' col-lg-' + this.labellg;
14821                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14822             }
14823             
14824             if(this.labelmd > 0){
14825                 labelCfg.cls += ' col-md-' + this.labelmd;
14826                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14827             }
14828             
14829             if(this.labelsm > 0){
14830                 labelCfg.cls += ' col-sm-' + this.labelsm;
14831                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14832             }
14833             
14834             if(this.labelxs > 0){
14835                 labelCfg.cls += ' col-xs-' + this.labelxs;
14836                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14837             }
14838                 
14839                 
14840         } else if ( this.fieldLabel.length) {
14841             cfg.cn = [
14842                 {
14843                    tag : 'i',
14844                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14845                    tooltip : 'This field is required'
14846                 },
14847                 {
14848                     tag: 'label',
14849                     cls : 'control-label',
14850                     html : this.fieldLabel
14851
14852                 },
14853                 {
14854                     cls : '', 
14855                     cn: [
14856                         combobox
14857                     ]
14858                 }
14859             ];
14860             
14861             if(this.indicatorpos == 'right'){
14862                 cfg.cn = [
14863                     {
14864                         tag: 'label',
14865                         cls : 'control-label',
14866                         html : this.fieldLabel,
14867                         cn : [
14868                             {
14869                                tag : 'i',
14870                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14871                                tooltip : 'This field is required'
14872                             }
14873                         ]
14874                     },
14875                     {
14876                         cls : '', 
14877                         cn: [
14878                             combobox
14879                         ]
14880                     }
14881                 ];
14882             }
14883         } else {
14884             cfg.cn = combobox;    
14885         }
14886         
14887         
14888         var settings = this;
14889         
14890         ['xs','sm','md','lg'].map(function(size){
14891             if (settings[size]) {
14892                 cfg.cls += ' col-' + size + '-' + settings[size];
14893             }
14894         });
14895         
14896         return cfg;
14897     },
14898     
14899     initTouchView : function()
14900     {
14901         this.renderTouchView();
14902         
14903         this.touchViewEl.on('scroll', function(){
14904             this.el.dom.scrollTop = 0;
14905         }, this);
14906         
14907         this.originalValue = this.getValue();
14908         
14909         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14910         
14911         this.inputEl().on("click", this.showTouchView, this);
14912         if (this.triggerEl) {
14913             this.triggerEl.on("click", this.showTouchView, this);
14914         }
14915         
14916         
14917         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14918         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14919         
14920         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14921         
14922         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14923         this.store.on('load', this.onTouchViewLoad, this);
14924         this.store.on('loadexception', this.onTouchViewLoadException, this);
14925         
14926         if(this.hiddenName){
14927             
14928             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14929             
14930             this.hiddenField.dom.value =
14931                 this.hiddenValue !== undefined ? this.hiddenValue :
14932                 this.value !== undefined ? this.value : '';
14933         
14934             this.el.dom.removeAttribute('name');
14935             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14936         }
14937         
14938         if(this.multiple){
14939             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14940             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14941         }
14942         
14943         if(this.removable && !this.multiple){
14944             var close = this.closeTriggerEl();
14945             if(close){
14946                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14947                 close.on('click', this.removeBtnClick, this, close);
14948             }
14949         }
14950         /*
14951          * fix the bug in Safari iOS8
14952          */
14953         this.inputEl().on("focus", function(e){
14954             document.activeElement.blur();
14955         }, this);
14956         
14957         return;
14958         
14959         
14960     },
14961     
14962     renderTouchView : function()
14963     {
14964         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14965         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14966         
14967         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14968         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14969         
14970         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14971         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14972         this.touchViewBodyEl.setStyle('overflow', 'auto');
14973         
14974         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14975         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14976         
14977         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14978         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14979         
14980     },
14981     
14982     showTouchView : function()
14983     {
14984         if(this.disabled){
14985             return;
14986         }
14987         
14988         this.touchViewHeaderEl.hide();
14989
14990         if(this.modalTitle.length){
14991             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14992             this.touchViewHeaderEl.show();
14993         }
14994
14995         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14996         this.touchViewEl.show();
14997
14998         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14999         
15000         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15001         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15002
15003         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15004
15005         if(this.modalTitle.length){
15006             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15007         }
15008         
15009         this.touchViewBodyEl.setHeight(bodyHeight);
15010
15011         if(this.animate){
15012             var _this = this;
15013             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15014         }else{
15015             this.touchViewEl.addClass('in');
15016         }
15017
15018         this.doTouchViewQuery();
15019         
15020     },
15021     
15022     hideTouchView : function()
15023     {
15024         this.touchViewEl.removeClass('in');
15025
15026         if(this.animate){
15027             var _this = this;
15028             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15029         }else{
15030             this.touchViewEl.setStyle('display', 'none');
15031         }
15032         
15033     },
15034     
15035     setTouchViewValue : function()
15036     {
15037         if(this.multiple){
15038             this.clearItem();
15039         
15040             var _this = this;
15041
15042             Roo.each(this.tickItems, function(o){
15043                 this.addItem(o);
15044             }, this);
15045         }
15046         
15047         this.hideTouchView();
15048     },
15049     
15050     doTouchViewQuery : function()
15051     {
15052         var qe = {
15053             query: '',
15054             forceAll: true,
15055             combo: this,
15056             cancel:false
15057         };
15058         
15059         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15060             return false;
15061         }
15062         
15063         if(!this.alwaysQuery || this.mode == 'local'){
15064             this.onTouchViewLoad();
15065             return;
15066         }
15067         
15068         this.store.load();
15069     },
15070     
15071     onTouchViewBeforeLoad : function(combo,opts)
15072     {
15073         return;
15074     },
15075
15076     // private
15077     onTouchViewLoad : function()
15078     {
15079         if(this.store.getCount() < 1){
15080             this.onTouchViewEmptyResults();
15081             return;
15082         }
15083         
15084         this.clearTouchView();
15085         
15086         var rawValue = this.getRawValue();
15087         
15088         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15089         
15090         this.tickItems = [];
15091         
15092         this.store.data.each(function(d, rowIndex){
15093             var row = this.touchViewListGroup.createChild(template);
15094             
15095             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15096                 row.addClass(d.data.cls);
15097             }
15098             
15099             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15100                 var cfg = {
15101                     data : d.data,
15102                     html : d.data[this.displayField]
15103                 };
15104                 
15105                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15106                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15107                 }
15108             }
15109             row.removeClass('selected');
15110             if(!this.multiple && this.valueField &&
15111                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15112             {
15113                 // radio buttons..
15114                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15115                 row.addClass('selected');
15116             }
15117             
15118             if(this.multiple && this.valueField &&
15119                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15120             {
15121                 
15122                 // checkboxes...
15123                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15124                 this.tickItems.push(d.data);
15125             }
15126             
15127             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15128             
15129         }, this);
15130         
15131         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15132         
15133         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15134
15135         if(this.modalTitle.length){
15136             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15137         }
15138
15139         var listHeight = this.touchViewListGroup.getHeight();
15140         
15141         var _this = this;
15142         
15143         if(firstChecked && listHeight > bodyHeight){
15144             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15145         }
15146         
15147     },
15148     
15149     onTouchViewLoadException : function()
15150     {
15151         this.hideTouchView();
15152     },
15153     
15154     onTouchViewEmptyResults : function()
15155     {
15156         this.clearTouchView();
15157         
15158         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15159         
15160         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15161         
15162     },
15163     
15164     clearTouchView : function()
15165     {
15166         this.touchViewListGroup.dom.innerHTML = '';
15167     },
15168     
15169     onTouchViewClick : function(e, el, o)
15170     {
15171         e.preventDefault();
15172         
15173         var row = o.row;
15174         var rowIndex = o.rowIndex;
15175         
15176         var r = this.store.getAt(rowIndex);
15177         
15178         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15179             
15180             if(!this.multiple){
15181                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15182                     c.dom.removeAttribute('checked');
15183                 }, this);
15184
15185                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15186
15187                 this.setFromData(r.data);
15188
15189                 var close = this.closeTriggerEl();
15190
15191                 if(close){
15192                     close.show();
15193                 }
15194
15195                 this.hideTouchView();
15196
15197                 this.fireEvent('select', this, r, rowIndex);
15198
15199                 return;
15200             }
15201
15202             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15203                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15204                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15205                 return;
15206             }
15207
15208             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15209             this.addItem(r.data);
15210             this.tickItems.push(r.data);
15211         }
15212     },
15213     
15214     getAutoCreateNativeIOS : function()
15215     {
15216         var cfg = {
15217             cls: 'form-group' //input-group,
15218         };
15219         
15220         var combobox =  {
15221             tag: 'select',
15222             cls : 'roo-ios-select'
15223         };
15224         
15225         if (this.name) {
15226             combobox.name = this.name;
15227         }
15228         
15229         if (this.disabled) {
15230             combobox.disabled = true;
15231         }
15232         
15233         var settings = this;
15234         
15235         ['xs','sm','md','lg'].map(function(size){
15236             if (settings[size]) {
15237                 cfg.cls += ' col-' + size + '-' + settings[size];
15238             }
15239         });
15240         
15241         cfg.cn = combobox;
15242         
15243         return cfg;
15244         
15245     },
15246     
15247     initIOSView : function()
15248     {
15249         this.store.on('load', this.onIOSViewLoad, this);
15250         
15251         return;
15252     },
15253     
15254     onIOSViewLoad : function()
15255     {
15256         if(this.store.getCount() < 1){
15257             return;
15258         }
15259         
15260         this.clearIOSView();
15261         
15262         if(this.allowBlank) {
15263             
15264             var default_text = '-- SELECT --';
15265             
15266             if(this.placeholder.length){
15267                 default_text = this.placeholder;
15268             }
15269             
15270             if(this.emptyTitle.length){
15271                 default_text += ' - ' + this.emptyTitle + ' -';
15272             }
15273             
15274             var opt = this.inputEl().createChild({
15275                 tag: 'option',
15276                 value : 0,
15277                 html : default_text
15278             });
15279             
15280             var o = {};
15281             o[this.valueField] = 0;
15282             o[this.displayField] = default_text;
15283             
15284             this.ios_options.push({
15285                 data : o,
15286                 el : opt
15287             });
15288             
15289         }
15290         
15291         this.store.data.each(function(d, rowIndex){
15292             
15293             var html = '';
15294             
15295             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15296                 html = d.data[this.displayField];
15297             }
15298             
15299             var value = '';
15300             
15301             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15302                 value = d.data[this.valueField];
15303             }
15304             
15305             var option = {
15306                 tag: 'option',
15307                 value : value,
15308                 html : html
15309             };
15310             
15311             if(this.value == d.data[this.valueField]){
15312                 option['selected'] = true;
15313             }
15314             
15315             var opt = this.inputEl().createChild(option);
15316             
15317             this.ios_options.push({
15318                 data : d.data,
15319                 el : opt
15320             });
15321             
15322         }, this);
15323         
15324         this.inputEl().on('change', function(){
15325            this.fireEvent('select', this);
15326         }, this);
15327         
15328     },
15329     
15330     clearIOSView: function()
15331     {
15332         this.inputEl().dom.innerHTML = '';
15333         
15334         this.ios_options = [];
15335     },
15336     
15337     setIOSValue: function(v)
15338     {
15339         this.value = v;
15340         
15341         if(!this.ios_options){
15342             return;
15343         }
15344         
15345         Roo.each(this.ios_options, function(opts){
15346            
15347            opts.el.dom.removeAttribute('selected');
15348            
15349            if(opts.data[this.valueField] != v){
15350                return;
15351            }
15352            
15353            opts.el.dom.setAttribute('selected', true);
15354            
15355         }, this);
15356     }
15357
15358     /** 
15359     * @cfg {Boolean} grow 
15360     * @hide 
15361     */
15362     /** 
15363     * @cfg {Number} growMin 
15364     * @hide 
15365     */
15366     /** 
15367     * @cfg {Number} growMax 
15368     * @hide 
15369     */
15370     /**
15371      * @hide
15372      * @method autoSize
15373      */
15374 });
15375
15376 Roo.apply(Roo.bootstrap.ComboBox,  {
15377     
15378     header : {
15379         tag: 'div',
15380         cls: 'modal-header',
15381         cn: [
15382             {
15383                 tag: 'h4',
15384                 cls: 'modal-title'
15385             }
15386         ]
15387     },
15388     
15389     body : {
15390         tag: 'div',
15391         cls: 'modal-body',
15392         cn: [
15393             {
15394                 tag: 'ul',
15395                 cls: 'list-group'
15396             }
15397         ]
15398     },
15399     
15400     listItemRadio : {
15401         tag: 'li',
15402         cls: 'list-group-item',
15403         cn: [
15404             {
15405                 tag: 'span',
15406                 cls: 'roo-combobox-list-group-item-value'
15407             },
15408             {
15409                 tag: 'div',
15410                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15411                 cn: [
15412                     {
15413                         tag: 'input',
15414                         type: 'radio'
15415                     },
15416                     {
15417                         tag: 'label'
15418                     }
15419                 ]
15420             }
15421         ]
15422     },
15423     
15424     listItemCheckbox : {
15425         tag: 'li',
15426         cls: 'list-group-item',
15427         cn: [
15428             {
15429                 tag: 'span',
15430                 cls: 'roo-combobox-list-group-item-value'
15431             },
15432             {
15433                 tag: 'div',
15434                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15435                 cn: [
15436                     {
15437                         tag: 'input',
15438                         type: 'checkbox'
15439                     },
15440                     {
15441                         tag: 'label'
15442                     }
15443                 ]
15444             }
15445         ]
15446     },
15447     
15448     emptyResult : {
15449         tag: 'div',
15450         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15451     },
15452     
15453     footer : {
15454         tag: 'div',
15455         cls: 'modal-footer',
15456         cn: [
15457             {
15458                 tag: 'div',
15459                 cls: 'row',
15460                 cn: [
15461                     {
15462                         tag: 'div',
15463                         cls: 'col-xs-6 text-left',
15464                         cn: {
15465                             tag: 'button',
15466                             cls: 'btn btn-danger roo-touch-view-cancel',
15467                             html: 'Cancel'
15468                         }
15469                     },
15470                     {
15471                         tag: 'div',
15472                         cls: 'col-xs-6 text-right',
15473                         cn: {
15474                             tag: 'button',
15475                             cls: 'btn btn-success roo-touch-view-ok',
15476                             html: 'OK'
15477                         }
15478                     }
15479                 ]
15480             }
15481         ]
15482         
15483     }
15484 });
15485
15486 Roo.apply(Roo.bootstrap.ComboBox,  {
15487     
15488     touchViewTemplate : {
15489         tag: 'div',
15490         cls: 'modal fade roo-combobox-touch-view',
15491         cn: [
15492             {
15493                 tag: 'div',
15494                 cls: 'modal-dialog',
15495                 style : 'position:fixed', // we have to fix position....
15496                 cn: [
15497                     {
15498                         tag: 'div',
15499                         cls: 'modal-content',
15500                         cn: [
15501                             Roo.bootstrap.ComboBox.header,
15502                             Roo.bootstrap.ComboBox.body,
15503                             Roo.bootstrap.ComboBox.footer
15504                         ]
15505                     }
15506                 ]
15507             }
15508         ]
15509     }
15510 });/*
15511  * Based on:
15512  * Ext JS Library 1.1.1
15513  * Copyright(c) 2006-2007, Ext JS, LLC.
15514  *
15515  * Originally Released Under LGPL - original licence link has changed is not relivant.
15516  *
15517  * Fork - LGPL
15518  * <script type="text/javascript">
15519  */
15520
15521 /**
15522  * @class Roo.View
15523  * @extends Roo.util.Observable
15524  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15525  * This class also supports single and multi selection modes. <br>
15526  * Create a data model bound view:
15527  <pre><code>
15528  var store = new Roo.data.Store(...);
15529
15530  var view = new Roo.View({
15531     el : "my-element",
15532     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15533  
15534     singleSelect: true,
15535     selectedClass: "ydataview-selected",
15536     store: store
15537  });
15538
15539  // listen for node click?
15540  view.on("click", function(vw, index, node, e){
15541  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15542  });
15543
15544  // load XML data
15545  dataModel.load("foobar.xml");
15546  </code></pre>
15547  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15548  * <br><br>
15549  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15550  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15551  * 
15552  * Note: old style constructor is still suported (container, template, config)
15553  * 
15554  * @constructor
15555  * Create a new View
15556  * @param {Object} config The config object
15557  * 
15558  */
15559 Roo.View = function(config, depreciated_tpl, depreciated_config){
15560     
15561     this.parent = false;
15562     
15563     if (typeof(depreciated_tpl) == 'undefined') {
15564         // new way.. - universal constructor.
15565         Roo.apply(this, config);
15566         this.el  = Roo.get(this.el);
15567     } else {
15568         // old format..
15569         this.el  = Roo.get(config);
15570         this.tpl = depreciated_tpl;
15571         Roo.apply(this, depreciated_config);
15572     }
15573     this.wrapEl  = this.el.wrap().wrap();
15574     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15575     
15576     
15577     if(typeof(this.tpl) == "string"){
15578         this.tpl = new Roo.Template(this.tpl);
15579     } else {
15580         // support xtype ctors..
15581         this.tpl = new Roo.factory(this.tpl, Roo);
15582     }
15583     
15584     
15585     this.tpl.compile();
15586     
15587     /** @private */
15588     this.addEvents({
15589         /**
15590          * @event beforeclick
15591          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
15598         /**
15599          * @event click
15600          * Fires when a template node is 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             "click" : true,
15607         /**
15608          * @event dblclick
15609          * Fires when a template node is double clicked.
15610          * @param {Roo.View} this
15611          * @param {Number} index The index of the target node
15612          * @param {HTMLElement} node The target node
15613          * @param {Roo.EventObject} e The raw event object
15614          */
15615             "dblclick" : true,
15616         /**
15617          * @event contextmenu
15618          * Fires when a template node is right clicked.
15619          * @param {Roo.View} this
15620          * @param {Number} index The index of the target node
15621          * @param {HTMLElement} node The target node
15622          * @param {Roo.EventObject} e The raw event object
15623          */
15624             "contextmenu" : true,
15625         /**
15626          * @event selectionchange
15627          * Fires when the selected nodes change.
15628          * @param {Roo.View} this
15629          * @param {Array} selections Array of the selected nodes
15630          */
15631             "selectionchange" : true,
15632     
15633         /**
15634          * @event beforeselect
15635          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15636          * @param {Roo.View} this
15637          * @param {HTMLElement} node The node to be selected
15638          * @param {Array} selections Array of currently selected nodes
15639          */
15640             "beforeselect" : true,
15641         /**
15642          * @event preparedata
15643          * Fires on every row to render, to allow you to change the data.
15644          * @param {Roo.View} this
15645          * @param {Object} data to be rendered (change this)
15646          */
15647           "preparedata" : true
15648           
15649           
15650         });
15651
15652
15653
15654     this.el.on({
15655         "click": this.onClick,
15656         "dblclick": this.onDblClick,
15657         "contextmenu": this.onContextMenu,
15658         scope:this
15659     });
15660
15661     this.selections = [];
15662     this.nodes = [];
15663     this.cmp = new Roo.CompositeElementLite([]);
15664     if(this.store){
15665         this.store = Roo.factory(this.store, Roo.data);
15666         this.setStore(this.store, true);
15667     }
15668     
15669     if ( this.footer && this.footer.xtype) {
15670            
15671          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15672         
15673         this.footer.dataSource = this.store;
15674         this.footer.container = fctr;
15675         this.footer = Roo.factory(this.footer, Roo);
15676         fctr.insertFirst(this.el);
15677         
15678         // this is a bit insane - as the paging toolbar seems to detach the el..
15679 //        dom.parentNode.parentNode.parentNode
15680          // they get detached?
15681     }
15682     
15683     
15684     Roo.View.superclass.constructor.call(this);
15685     
15686     
15687 };
15688
15689 Roo.extend(Roo.View, Roo.util.Observable, {
15690     
15691      /**
15692      * @cfg {Roo.data.Store} store Data store to load data from.
15693      */
15694     store : false,
15695     
15696     /**
15697      * @cfg {String|Roo.Element} el The container element.
15698      */
15699     el : '',
15700     
15701     /**
15702      * @cfg {String|Roo.Template} tpl The template used by this View 
15703      */
15704     tpl : false,
15705     /**
15706      * @cfg {String} dataName the named area of the template to use as the data area
15707      *                          Works with domtemplates roo-name="name"
15708      */
15709     dataName: false,
15710     /**
15711      * @cfg {String} selectedClass The css class to add to selected nodes
15712      */
15713     selectedClass : "x-view-selected",
15714      /**
15715      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15716      */
15717     emptyText : "",
15718     
15719     /**
15720      * @cfg {String} text to display on mask (default Loading)
15721      */
15722     mask : false,
15723     /**
15724      * @cfg {Boolean} multiSelect Allow multiple selection
15725      */
15726     multiSelect : false,
15727     /**
15728      * @cfg {Boolean} singleSelect Allow single selection
15729      */
15730     singleSelect:  false,
15731     
15732     /**
15733      * @cfg {Boolean} toggleSelect - selecting 
15734      */
15735     toggleSelect : false,
15736     
15737     /**
15738      * @cfg {Boolean} tickable - selecting 
15739      */
15740     tickable : false,
15741     
15742     /**
15743      * Returns the element this view is bound to.
15744      * @return {Roo.Element}
15745      */
15746     getEl : function(){
15747         return this.wrapEl;
15748     },
15749     
15750     
15751
15752     /**
15753      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15754      */
15755     refresh : function(){
15756         //Roo.log('refresh');
15757         var t = this.tpl;
15758         
15759         // if we are using something like 'domtemplate', then
15760         // the what gets used is:
15761         // t.applySubtemplate(NAME, data, wrapping data..)
15762         // the outer template then get' applied with
15763         //     the store 'extra data'
15764         // and the body get's added to the
15765         //      roo-name="data" node?
15766         //      <span class='roo-tpl-{name}'></span> ?????
15767         
15768         
15769         
15770         this.clearSelections();
15771         this.el.update("");
15772         var html = [];
15773         var records = this.store.getRange();
15774         if(records.length < 1) {
15775             
15776             // is this valid??  = should it render a template??
15777             
15778             this.el.update(this.emptyText);
15779             return;
15780         }
15781         var el = this.el;
15782         if (this.dataName) {
15783             this.el.update(t.apply(this.store.meta)); //????
15784             el = this.el.child('.roo-tpl-' + this.dataName);
15785         }
15786         
15787         for(var i = 0, len = records.length; i < len; i++){
15788             var data = this.prepareData(records[i].data, i, records[i]);
15789             this.fireEvent("preparedata", this, data, i, records[i]);
15790             
15791             var d = Roo.apply({}, data);
15792             
15793             if(this.tickable){
15794                 Roo.apply(d, {'roo-id' : Roo.id()});
15795                 
15796                 var _this = this;
15797             
15798                 Roo.each(this.parent.item, function(item){
15799                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15800                         return;
15801                     }
15802                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15803                 });
15804             }
15805             
15806             html[html.length] = Roo.util.Format.trim(
15807                 this.dataName ?
15808                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15809                     t.apply(d)
15810             );
15811         }
15812         
15813         
15814         
15815         el.update(html.join(""));
15816         this.nodes = el.dom.childNodes;
15817         this.updateIndexes(0);
15818     },
15819     
15820
15821     /**
15822      * Function to override to reformat the data that is sent to
15823      * the template for each node.
15824      * DEPRICATED - use the preparedata event handler.
15825      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15826      * a JSON object for an UpdateManager bound view).
15827      */
15828     prepareData : function(data, index, record)
15829     {
15830         this.fireEvent("preparedata", this, data, index, record);
15831         return data;
15832     },
15833
15834     onUpdate : function(ds, record){
15835         // Roo.log('on update');   
15836         this.clearSelections();
15837         var index = this.store.indexOf(record);
15838         var n = this.nodes[index];
15839         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15840         n.parentNode.removeChild(n);
15841         this.updateIndexes(index, index);
15842     },
15843
15844     
15845     
15846 // --------- FIXME     
15847     onAdd : function(ds, records, index)
15848     {
15849         //Roo.log(['on Add', ds, records, index] );        
15850         this.clearSelections();
15851         if(this.nodes.length == 0){
15852             this.refresh();
15853             return;
15854         }
15855         var n = this.nodes[index];
15856         for(var i = 0, len = records.length; i < len; i++){
15857             var d = this.prepareData(records[i].data, i, records[i]);
15858             if(n){
15859                 this.tpl.insertBefore(n, d);
15860             }else{
15861                 
15862                 this.tpl.append(this.el, d);
15863             }
15864         }
15865         this.updateIndexes(index);
15866     },
15867
15868     onRemove : function(ds, record, index){
15869        // Roo.log('onRemove');
15870         this.clearSelections();
15871         var el = this.dataName  ?
15872             this.el.child('.roo-tpl-' + this.dataName) :
15873             this.el; 
15874         
15875         el.dom.removeChild(this.nodes[index]);
15876         this.updateIndexes(index);
15877     },
15878
15879     /**
15880      * Refresh an individual node.
15881      * @param {Number} index
15882      */
15883     refreshNode : function(index){
15884         this.onUpdate(this.store, this.store.getAt(index));
15885     },
15886
15887     updateIndexes : function(startIndex, endIndex){
15888         var ns = this.nodes;
15889         startIndex = startIndex || 0;
15890         endIndex = endIndex || ns.length - 1;
15891         for(var i = startIndex; i <= endIndex; i++){
15892             ns[i].nodeIndex = i;
15893         }
15894     },
15895
15896     /**
15897      * Changes the data store this view uses and refresh the view.
15898      * @param {Store} store
15899      */
15900     setStore : function(store, initial){
15901         if(!initial && this.store){
15902             this.store.un("datachanged", this.refresh);
15903             this.store.un("add", this.onAdd);
15904             this.store.un("remove", this.onRemove);
15905             this.store.un("update", this.onUpdate);
15906             this.store.un("clear", this.refresh);
15907             this.store.un("beforeload", this.onBeforeLoad);
15908             this.store.un("load", this.onLoad);
15909             this.store.un("loadexception", this.onLoad);
15910         }
15911         if(store){
15912           
15913             store.on("datachanged", this.refresh, this);
15914             store.on("add", this.onAdd, this);
15915             store.on("remove", this.onRemove, this);
15916             store.on("update", this.onUpdate, this);
15917             store.on("clear", this.refresh, this);
15918             store.on("beforeload", this.onBeforeLoad, this);
15919             store.on("load", this.onLoad, this);
15920             store.on("loadexception", this.onLoad, this);
15921         }
15922         
15923         if(store){
15924             this.refresh();
15925         }
15926     },
15927     /**
15928      * onbeforeLoad - masks the loading area.
15929      *
15930      */
15931     onBeforeLoad : function(store,opts)
15932     {
15933          //Roo.log('onBeforeLoad');   
15934         if (!opts.add) {
15935             this.el.update("");
15936         }
15937         this.el.mask(this.mask ? this.mask : "Loading" ); 
15938     },
15939     onLoad : function ()
15940     {
15941         this.el.unmask();
15942     },
15943     
15944
15945     /**
15946      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15947      * @param {HTMLElement} node
15948      * @return {HTMLElement} The template node
15949      */
15950     findItemFromChild : function(node){
15951         var el = this.dataName  ?
15952             this.el.child('.roo-tpl-' + this.dataName,true) :
15953             this.el.dom; 
15954         
15955         if(!node || node.parentNode == el){
15956                     return node;
15957             }
15958             var p = node.parentNode;
15959             while(p && p != el){
15960             if(p.parentNode == el){
15961                 return p;
15962             }
15963             p = p.parentNode;
15964         }
15965             return null;
15966     },
15967
15968     /** @ignore */
15969     onClick : function(e){
15970         var item = this.findItemFromChild(e.getTarget());
15971         if(item){
15972             var index = this.indexOf(item);
15973             if(this.onItemClick(item, index, e) !== false){
15974                 this.fireEvent("click", this, index, item, e);
15975             }
15976         }else{
15977             this.clearSelections();
15978         }
15979     },
15980
15981     /** @ignore */
15982     onContextMenu : function(e){
15983         var item = this.findItemFromChild(e.getTarget());
15984         if(item){
15985             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15986         }
15987     },
15988
15989     /** @ignore */
15990     onDblClick : function(e){
15991         var item = this.findItemFromChild(e.getTarget());
15992         if(item){
15993             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15994         }
15995     },
15996
15997     onItemClick : function(item, index, e)
15998     {
15999         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16000             return false;
16001         }
16002         if (this.toggleSelect) {
16003             var m = this.isSelected(item) ? 'unselect' : 'select';
16004             //Roo.log(m);
16005             var _t = this;
16006             _t[m](item, true, false);
16007             return true;
16008         }
16009         if(this.multiSelect || this.singleSelect){
16010             if(this.multiSelect && e.shiftKey && this.lastSelection){
16011                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16012             }else{
16013                 this.select(item, this.multiSelect && e.ctrlKey);
16014                 this.lastSelection = item;
16015             }
16016             
16017             if(!this.tickable){
16018                 e.preventDefault();
16019             }
16020             
16021         }
16022         return true;
16023     },
16024
16025     /**
16026      * Get the number of selected nodes.
16027      * @return {Number}
16028      */
16029     getSelectionCount : function(){
16030         return this.selections.length;
16031     },
16032
16033     /**
16034      * Get the currently selected nodes.
16035      * @return {Array} An array of HTMLElements
16036      */
16037     getSelectedNodes : function(){
16038         return this.selections;
16039     },
16040
16041     /**
16042      * Get the indexes of the selected nodes.
16043      * @return {Array}
16044      */
16045     getSelectedIndexes : function(){
16046         var indexes = [], s = this.selections;
16047         for(var i = 0, len = s.length; i < len; i++){
16048             indexes.push(s[i].nodeIndex);
16049         }
16050         return indexes;
16051     },
16052
16053     /**
16054      * Clear all selections
16055      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16056      */
16057     clearSelections : function(suppressEvent){
16058         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16059             this.cmp.elements = this.selections;
16060             this.cmp.removeClass(this.selectedClass);
16061             this.selections = [];
16062             if(!suppressEvent){
16063                 this.fireEvent("selectionchange", this, this.selections);
16064             }
16065         }
16066     },
16067
16068     /**
16069      * Returns true if the passed node is selected
16070      * @param {HTMLElement/Number} node The node or node index
16071      * @return {Boolean}
16072      */
16073     isSelected : function(node){
16074         var s = this.selections;
16075         if(s.length < 1){
16076             return false;
16077         }
16078         node = this.getNode(node);
16079         return s.indexOf(node) !== -1;
16080     },
16081
16082     /**
16083      * Selects nodes.
16084      * @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
16085      * @param {Boolean} keepExisting (optional) true to keep existing selections
16086      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16087      */
16088     select : function(nodeInfo, keepExisting, suppressEvent){
16089         if(nodeInfo instanceof Array){
16090             if(!keepExisting){
16091                 this.clearSelections(true);
16092             }
16093             for(var i = 0, len = nodeInfo.length; i < len; i++){
16094                 this.select(nodeInfo[i], true, true);
16095             }
16096             return;
16097         } 
16098         var node = this.getNode(nodeInfo);
16099         if(!node || this.isSelected(node)){
16100             return; // already selected.
16101         }
16102         if(!keepExisting){
16103             this.clearSelections(true);
16104         }
16105         
16106         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16107             Roo.fly(node).addClass(this.selectedClass);
16108             this.selections.push(node);
16109             if(!suppressEvent){
16110                 this.fireEvent("selectionchange", this, this.selections);
16111             }
16112         }
16113         
16114         
16115     },
16116       /**
16117      * Unselects nodes.
16118      * @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
16119      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16120      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16121      */
16122     unselect : function(nodeInfo, keepExisting, suppressEvent)
16123     {
16124         if(nodeInfo instanceof Array){
16125             Roo.each(this.selections, function(s) {
16126                 this.unselect(s, nodeInfo);
16127             }, this);
16128             return;
16129         }
16130         var node = this.getNode(nodeInfo);
16131         if(!node || !this.isSelected(node)){
16132             //Roo.log("not selected");
16133             return; // not selected.
16134         }
16135         // fireevent???
16136         var ns = [];
16137         Roo.each(this.selections, function(s) {
16138             if (s == node ) {
16139                 Roo.fly(node).removeClass(this.selectedClass);
16140
16141                 return;
16142             }
16143             ns.push(s);
16144         },this);
16145         
16146         this.selections= ns;
16147         this.fireEvent("selectionchange", this, this.selections);
16148     },
16149
16150     /**
16151      * Gets a template node.
16152      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16153      * @return {HTMLElement} The node or null if it wasn't found
16154      */
16155     getNode : function(nodeInfo){
16156         if(typeof nodeInfo == "string"){
16157             return document.getElementById(nodeInfo);
16158         }else if(typeof nodeInfo == "number"){
16159             return this.nodes[nodeInfo];
16160         }
16161         return nodeInfo;
16162     },
16163
16164     /**
16165      * Gets a range template nodes.
16166      * @param {Number} startIndex
16167      * @param {Number} endIndex
16168      * @return {Array} An array of nodes
16169      */
16170     getNodes : function(start, end){
16171         var ns = this.nodes;
16172         start = start || 0;
16173         end = typeof end == "undefined" ? ns.length - 1 : end;
16174         var nodes = [];
16175         if(start <= end){
16176             for(var i = start; i <= end; i++){
16177                 nodes.push(ns[i]);
16178             }
16179         } else{
16180             for(var i = start; i >= end; i--){
16181                 nodes.push(ns[i]);
16182             }
16183         }
16184         return nodes;
16185     },
16186
16187     /**
16188      * Finds the index of the passed node
16189      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16190      * @return {Number} The index of the node or -1
16191      */
16192     indexOf : function(node){
16193         node = this.getNode(node);
16194         if(typeof node.nodeIndex == "number"){
16195             return node.nodeIndex;
16196         }
16197         var ns = this.nodes;
16198         for(var i = 0, len = ns.length; i < len; i++){
16199             if(ns[i] == node){
16200                 return i;
16201             }
16202         }
16203         return -1;
16204     }
16205 });
16206 /*
16207  * - LGPL
16208  *
16209  * based on jquery fullcalendar
16210  * 
16211  */
16212
16213 Roo.bootstrap = Roo.bootstrap || {};
16214 /**
16215  * @class Roo.bootstrap.Calendar
16216  * @extends Roo.bootstrap.Component
16217  * Bootstrap Calendar class
16218  * @cfg {Boolean} loadMask (true|false) default false
16219  * @cfg {Object} header generate the user specific header of the calendar, default false
16220
16221  * @constructor
16222  * Create a new Container
16223  * @param {Object} config The config object
16224  */
16225
16226
16227
16228 Roo.bootstrap.Calendar = function(config){
16229     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16230      this.addEvents({
16231         /**
16232              * @event select
16233              * Fires when a date is selected
16234              * @param {DatePicker} this
16235              * @param {Date} date The selected date
16236              */
16237         'select': true,
16238         /**
16239              * @event monthchange
16240              * Fires when the displayed month changes 
16241              * @param {DatePicker} this
16242              * @param {Date} date The selected month
16243              */
16244         'monthchange': true,
16245         /**
16246              * @event evententer
16247              * Fires when mouse over an event
16248              * @param {Calendar} this
16249              * @param {event} Event
16250              */
16251         'evententer': true,
16252         /**
16253              * @event eventleave
16254              * Fires when the mouse leaves an
16255              * @param {Calendar} this
16256              * @param {event}
16257              */
16258         'eventleave': true,
16259         /**
16260              * @event eventclick
16261              * Fires when the mouse click an
16262              * @param {Calendar} this
16263              * @param {event}
16264              */
16265         'eventclick': true
16266         
16267     });
16268
16269 };
16270
16271 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16272     
16273      /**
16274      * @cfg {Number} startDay
16275      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16276      */
16277     startDay : 0,
16278     
16279     loadMask : false,
16280     
16281     header : false,
16282       
16283     getAutoCreate : function(){
16284         
16285         
16286         var fc_button = function(name, corner, style, content ) {
16287             return Roo.apply({},{
16288                 tag : 'span',
16289                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16290                          (corner.length ?
16291                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16292                             ''
16293                         ),
16294                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16295                 unselectable: 'on'
16296             });
16297         };
16298         
16299         var header = {};
16300         
16301         if(!this.header){
16302             header = {
16303                 tag : 'table',
16304                 cls : 'fc-header',
16305                 style : 'width:100%',
16306                 cn : [
16307                     {
16308                         tag: 'tr',
16309                         cn : [
16310                             {
16311                                 tag : 'td',
16312                                 cls : 'fc-header-left',
16313                                 cn : [
16314                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16315                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16316                                     { tag: 'span', cls: 'fc-header-space' },
16317                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16318
16319
16320                                 ]
16321                             },
16322
16323                             {
16324                                 tag : 'td',
16325                                 cls : 'fc-header-center',
16326                                 cn : [
16327                                     {
16328                                         tag: 'span',
16329                                         cls: 'fc-header-title',
16330                                         cn : {
16331                                             tag: 'H2',
16332                                             html : 'month / year'
16333                                         }
16334                                     }
16335
16336                                 ]
16337                             },
16338                             {
16339                                 tag : 'td',
16340                                 cls : 'fc-header-right',
16341                                 cn : [
16342                               /*      fc_button('month', 'left', '', 'month' ),
16343                                     fc_button('week', '', '', 'week' ),
16344                                     fc_button('day', 'right', '', 'day' )
16345                                 */    
16346
16347                                 ]
16348                             }
16349
16350                         ]
16351                     }
16352                 ]
16353             };
16354         }
16355         
16356         header = this.header;
16357         
16358        
16359         var cal_heads = function() {
16360             var ret = [];
16361             // fixme - handle this.
16362             
16363             for (var i =0; i < Date.dayNames.length; i++) {
16364                 var d = Date.dayNames[i];
16365                 ret.push({
16366                     tag: 'th',
16367                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16368                     html : d.substring(0,3)
16369                 });
16370                 
16371             }
16372             ret[0].cls += ' fc-first';
16373             ret[6].cls += ' fc-last';
16374             return ret;
16375         };
16376         var cal_cell = function(n) {
16377             return  {
16378                 tag: 'td',
16379                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16380                 cn : [
16381                     {
16382                         cn : [
16383                             {
16384                                 cls: 'fc-day-number',
16385                                 html: 'D'
16386                             },
16387                             {
16388                                 cls: 'fc-day-content',
16389                              
16390                                 cn : [
16391                                      {
16392                                         style: 'position: relative;' // height: 17px;
16393                                     }
16394                                 ]
16395                             }
16396                             
16397                             
16398                         ]
16399                     }
16400                 ]
16401                 
16402             }
16403         };
16404         var cal_rows = function() {
16405             
16406             var ret = [];
16407             for (var r = 0; r < 6; r++) {
16408                 var row= {
16409                     tag : 'tr',
16410                     cls : 'fc-week',
16411                     cn : []
16412                 };
16413                 
16414                 for (var i =0; i < Date.dayNames.length; i++) {
16415                     var d = Date.dayNames[i];
16416                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16417
16418                 }
16419                 row.cn[0].cls+=' fc-first';
16420                 row.cn[0].cn[0].style = 'min-height:90px';
16421                 row.cn[6].cls+=' fc-last';
16422                 ret.push(row);
16423                 
16424             }
16425             ret[0].cls += ' fc-first';
16426             ret[4].cls += ' fc-prev-last';
16427             ret[5].cls += ' fc-last';
16428             return ret;
16429             
16430         };
16431         
16432         var cal_table = {
16433             tag: 'table',
16434             cls: 'fc-border-separate',
16435             style : 'width:100%',
16436             cellspacing  : 0,
16437             cn : [
16438                 { 
16439                     tag: 'thead',
16440                     cn : [
16441                         { 
16442                             tag: 'tr',
16443                             cls : 'fc-first fc-last',
16444                             cn : cal_heads()
16445                         }
16446                     ]
16447                 },
16448                 { 
16449                     tag: 'tbody',
16450                     cn : cal_rows()
16451                 }
16452                   
16453             ]
16454         };
16455          
16456          var cfg = {
16457             cls : 'fc fc-ltr',
16458             cn : [
16459                 header,
16460                 {
16461                     cls : 'fc-content',
16462                     style : "position: relative;",
16463                     cn : [
16464                         {
16465                             cls : 'fc-view fc-view-month fc-grid',
16466                             style : 'position: relative',
16467                             unselectable : 'on',
16468                             cn : [
16469                                 {
16470                                     cls : 'fc-event-container',
16471                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16472                                 },
16473                                 cal_table
16474                             ]
16475                         }
16476                     ]
16477     
16478                 }
16479            ] 
16480             
16481         };
16482         
16483          
16484         
16485         return cfg;
16486     },
16487     
16488     
16489     initEvents : function()
16490     {
16491         if(!this.store){
16492             throw "can not find store for calendar";
16493         }
16494         
16495         var mark = {
16496             tag: "div",
16497             cls:"x-dlg-mask",
16498             style: "text-align:center",
16499             cn: [
16500                 {
16501                     tag: "div",
16502                     style: "background-color:white;width:50%;margin:250 auto",
16503                     cn: [
16504                         {
16505                             tag: "img",
16506                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16507                         },
16508                         {
16509                             tag: "span",
16510                             html: "Loading"
16511                         }
16512                         
16513                     ]
16514                 }
16515             ]
16516         };
16517         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16518         
16519         var size = this.el.select('.fc-content', true).first().getSize();
16520         this.maskEl.setSize(size.width, size.height);
16521         this.maskEl.enableDisplayMode("block");
16522         if(!this.loadMask){
16523             this.maskEl.hide();
16524         }
16525         
16526         this.store = Roo.factory(this.store, Roo.data);
16527         this.store.on('load', this.onLoad, this);
16528         this.store.on('beforeload', this.onBeforeLoad, this);
16529         
16530         this.resize();
16531         
16532         this.cells = this.el.select('.fc-day',true);
16533         //Roo.log(this.cells);
16534         this.textNodes = this.el.query('.fc-day-number');
16535         this.cells.addClassOnOver('fc-state-hover');
16536         
16537         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16538         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16539         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16540         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16541         
16542         this.on('monthchange', this.onMonthChange, this);
16543         
16544         this.update(new Date().clearTime());
16545     },
16546     
16547     resize : function() {
16548         var sz  = this.el.getSize();
16549         
16550         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16551         this.el.select('.fc-day-content div',true).setHeight(34);
16552     },
16553     
16554     
16555     // private
16556     showPrevMonth : function(e){
16557         this.update(this.activeDate.add("mo", -1));
16558     },
16559     showToday : function(e){
16560         this.update(new Date().clearTime());
16561     },
16562     // private
16563     showNextMonth : function(e){
16564         this.update(this.activeDate.add("mo", 1));
16565     },
16566
16567     // private
16568     showPrevYear : function(){
16569         this.update(this.activeDate.add("y", -1));
16570     },
16571
16572     // private
16573     showNextYear : function(){
16574         this.update(this.activeDate.add("y", 1));
16575     },
16576
16577     
16578    // private
16579     update : function(date)
16580     {
16581         var vd = this.activeDate;
16582         this.activeDate = date;
16583 //        if(vd && this.el){
16584 //            var t = date.getTime();
16585 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16586 //                Roo.log('using add remove');
16587 //                
16588 //                this.fireEvent('monthchange', this, date);
16589 //                
16590 //                this.cells.removeClass("fc-state-highlight");
16591 //                this.cells.each(function(c){
16592 //                   if(c.dateValue == t){
16593 //                       c.addClass("fc-state-highlight");
16594 //                       setTimeout(function(){
16595 //                            try{c.dom.firstChild.focus();}catch(e){}
16596 //                       }, 50);
16597 //                       return false;
16598 //                   }
16599 //                   return true;
16600 //                });
16601 //                return;
16602 //            }
16603 //        }
16604         
16605         var days = date.getDaysInMonth();
16606         
16607         var firstOfMonth = date.getFirstDateOfMonth();
16608         var startingPos = firstOfMonth.getDay()-this.startDay;
16609         
16610         if(startingPos < this.startDay){
16611             startingPos += 7;
16612         }
16613         
16614         var pm = date.add(Date.MONTH, -1);
16615         var prevStart = pm.getDaysInMonth()-startingPos;
16616 //        
16617         this.cells = this.el.select('.fc-day',true);
16618         this.textNodes = this.el.query('.fc-day-number');
16619         this.cells.addClassOnOver('fc-state-hover');
16620         
16621         var cells = this.cells.elements;
16622         var textEls = this.textNodes;
16623         
16624         Roo.each(cells, function(cell){
16625             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16626         });
16627         
16628         days += startingPos;
16629
16630         // convert everything to numbers so it's fast
16631         var day = 86400000;
16632         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16633         //Roo.log(d);
16634         //Roo.log(pm);
16635         //Roo.log(prevStart);
16636         
16637         var today = new Date().clearTime().getTime();
16638         var sel = date.clearTime().getTime();
16639         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16640         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16641         var ddMatch = this.disabledDatesRE;
16642         var ddText = this.disabledDatesText;
16643         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16644         var ddaysText = this.disabledDaysText;
16645         var format = this.format;
16646         
16647         var setCellClass = function(cal, cell){
16648             cell.row = 0;
16649             cell.events = [];
16650             cell.more = [];
16651             //Roo.log('set Cell Class');
16652             cell.title = "";
16653             var t = d.getTime();
16654             
16655             //Roo.log(d);
16656             
16657             cell.dateValue = t;
16658             if(t == today){
16659                 cell.className += " fc-today";
16660                 cell.className += " fc-state-highlight";
16661                 cell.title = cal.todayText;
16662             }
16663             if(t == sel){
16664                 // disable highlight in other month..
16665                 //cell.className += " fc-state-highlight";
16666                 
16667             }
16668             // disabling
16669             if(t < min) {
16670                 cell.className = " fc-state-disabled";
16671                 cell.title = cal.minText;
16672                 return;
16673             }
16674             if(t > max) {
16675                 cell.className = " fc-state-disabled";
16676                 cell.title = cal.maxText;
16677                 return;
16678             }
16679             if(ddays){
16680                 if(ddays.indexOf(d.getDay()) != -1){
16681                     cell.title = ddaysText;
16682                     cell.className = " fc-state-disabled";
16683                 }
16684             }
16685             if(ddMatch && format){
16686                 var fvalue = d.dateFormat(format);
16687                 if(ddMatch.test(fvalue)){
16688                     cell.title = ddText.replace("%0", fvalue);
16689                     cell.className = " fc-state-disabled";
16690                 }
16691             }
16692             
16693             if (!cell.initialClassName) {
16694                 cell.initialClassName = cell.dom.className;
16695             }
16696             
16697             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16698         };
16699
16700         var i = 0;
16701         
16702         for(; i < startingPos; i++) {
16703             textEls[i].innerHTML = (++prevStart);
16704             d.setDate(d.getDate()+1);
16705             
16706             cells[i].className = "fc-past fc-other-month";
16707             setCellClass(this, cells[i]);
16708         }
16709         
16710         var intDay = 0;
16711         
16712         for(; i < days; i++){
16713             intDay = i - startingPos + 1;
16714             textEls[i].innerHTML = (intDay);
16715             d.setDate(d.getDate()+1);
16716             
16717             cells[i].className = ''; // "x-date-active";
16718             setCellClass(this, cells[i]);
16719         }
16720         var extraDays = 0;
16721         
16722         for(; i < 42; i++) {
16723             textEls[i].innerHTML = (++extraDays);
16724             d.setDate(d.getDate()+1);
16725             
16726             cells[i].className = "fc-future fc-other-month";
16727             setCellClass(this, cells[i]);
16728         }
16729         
16730         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16731         
16732         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16733         
16734         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16735         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16736         
16737         if(totalRows != 6){
16738             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16739             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16740         }
16741         
16742         this.fireEvent('monthchange', this, date);
16743         
16744         
16745         /*
16746         if(!this.internalRender){
16747             var main = this.el.dom.firstChild;
16748             var w = main.offsetWidth;
16749             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16750             Roo.fly(main).setWidth(w);
16751             this.internalRender = true;
16752             // opera does not respect the auto grow header center column
16753             // then, after it gets a width opera refuses to recalculate
16754             // without a second pass
16755             if(Roo.isOpera && !this.secondPass){
16756                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16757                 this.secondPass = true;
16758                 this.update.defer(10, this, [date]);
16759             }
16760         }
16761         */
16762         
16763     },
16764     
16765     findCell : function(dt) {
16766         dt = dt.clearTime().getTime();
16767         var ret = false;
16768         this.cells.each(function(c){
16769             //Roo.log("check " +c.dateValue + '?=' + dt);
16770             if(c.dateValue == dt){
16771                 ret = c;
16772                 return false;
16773             }
16774             return true;
16775         });
16776         
16777         return ret;
16778     },
16779     
16780     findCells : function(ev) {
16781         var s = ev.start.clone().clearTime().getTime();
16782        // Roo.log(s);
16783         var e= ev.end.clone().clearTime().getTime();
16784        // Roo.log(e);
16785         var ret = [];
16786         this.cells.each(function(c){
16787              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16788             
16789             if(c.dateValue > e){
16790                 return ;
16791             }
16792             if(c.dateValue < s){
16793                 return ;
16794             }
16795             ret.push(c);
16796         });
16797         
16798         return ret;    
16799     },
16800     
16801 //    findBestRow: function(cells)
16802 //    {
16803 //        var ret = 0;
16804 //        
16805 //        for (var i =0 ; i < cells.length;i++) {
16806 //            ret  = Math.max(cells[i].rows || 0,ret);
16807 //        }
16808 //        return ret;
16809 //        
16810 //    },
16811     
16812     
16813     addItem : function(ev)
16814     {
16815         // look for vertical location slot in
16816         var cells = this.findCells(ev);
16817         
16818 //        ev.row = this.findBestRow(cells);
16819         
16820         // work out the location.
16821         
16822         var crow = false;
16823         var rows = [];
16824         for(var i =0; i < cells.length; i++) {
16825             
16826             cells[i].row = cells[0].row;
16827             
16828             if(i == 0){
16829                 cells[i].row = cells[i].row + 1;
16830             }
16831             
16832             if (!crow) {
16833                 crow = {
16834                     start : cells[i],
16835                     end :  cells[i]
16836                 };
16837                 continue;
16838             }
16839             if (crow.start.getY() == cells[i].getY()) {
16840                 // on same row.
16841                 crow.end = cells[i];
16842                 continue;
16843             }
16844             // different row.
16845             rows.push(crow);
16846             crow = {
16847                 start: cells[i],
16848                 end : cells[i]
16849             };
16850             
16851         }
16852         
16853         rows.push(crow);
16854         ev.els = [];
16855         ev.rows = rows;
16856         ev.cells = cells;
16857         
16858         cells[0].events.push(ev);
16859         
16860         this.calevents.push(ev);
16861     },
16862     
16863     clearEvents: function() {
16864         
16865         if(!this.calevents){
16866             return;
16867         }
16868         
16869         Roo.each(this.cells.elements, function(c){
16870             c.row = 0;
16871             c.events = [];
16872             c.more = [];
16873         });
16874         
16875         Roo.each(this.calevents, function(e) {
16876             Roo.each(e.els, function(el) {
16877                 el.un('mouseenter' ,this.onEventEnter, this);
16878                 el.un('mouseleave' ,this.onEventLeave, this);
16879                 el.remove();
16880             },this);
16881         },this);
16882         
16883         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16884             e.remove();
16885         });
16886         
16887     },
16888     
16889     renderEvents: function()
16890     {   
16891         var _this = this;
16892         
16893         this.cells.each(function(c) {
16894             
16895             if(c.row < 5){
16896                 return;
16897             }
16898             
16899             var ev = c.events;
16900             
16901             var r = 4;
16902             if(c.row != c.events.length){
16903                 r = 4 - (4 - (c.row - c.events.length));
16904             }
16905             
16906             c.events = ev.slice(0, r);
16907             c.more = ev.slice(r);
16908             
16909             if(c.more.length && c.more.length == 1){
16910                 c.events.push(c.more.pop());
16911             }
16912             
16913             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16914             
16915         });
16916             
16917         this.cells.each(function(c) {
16918             
16919             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16920             
16921             
16922             for (var e = 0; e < c.events.length; e++){
16923                 var ev = c.events[e];
16924                 var rows = ev.rows;
16925                 
16926                 for(var i = 0; i < rows.length; i++) {
16927                 
16928                     // how many rows should it span..
16929
16930                     var  cfg = {
16931                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16932                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16933
16934                         unselectable : "on",
16935                         cn : [
16936                             {
16937                                 cls: 'fc-event-inner',
16938                                 cn : [
16939     //                                {
16940     //                                  tag:'span',
16941     //                                  cls: 'fc-event-time',
16942     //                                  html : cells.length > 1 ? '' : ev.time
16943     //                                },
16944                                     {
16945                                       tag:'span',
16946                                       cls: 'fc-event-title',
16947                                       html : String.format('{0}', ev.title)
16948                                     }
16949
16950
16951                                 ]
16952                             },
16953                             {
16954                                 cls: 'ui-resizable-handle ui-resizable-e',
16955                                 html : '&nbsp;&nbsp;&nbsp'
16956                             }
16957
16958                         ]
16959                     };
16960
16961                     if (i == 0) {
16962                         cfg.cls += ' fc-event-start';
16963                     }
16964                     if ((i+1) == rows.length) {
16965                         cfg.cls += ' fc-event-end';
16966                     }
16967
16968                     var ctr = _this.el.select('.fc-event-container',true).first();
16969                     var cg = ctr.createChild(cfg);
16970
16971                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16972                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16973
16974                     var r = (c.more.length) ? 1 : 0;
16975                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16976                     cg.setWidth(ebox.right - sbox.x -2);
16977
16978                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16979                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16980                     cg.on('click', _this.onEventClick, _this, ev);
16981
16982                     ev.els.push(cg);
16983                     
16984                 }
16985                 
16986             }
16987             
16988             
16989             if(c.more.length){
16990                 var  cfg = {
16991                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16992                     style : 'position: absolute',
16993                     unselectable : "on",
16994                     cn : [
16995                         {
16996                             cls: 'fc-event-inner',
16997                             cn : [
16998                                 {
16999                                   tag:'span',
17000                                   cls: 'fc-event-title',
17001                                   html : 'More'
17002                                 }
17003
17004
17005                             ]
17006                         },
17007                         {
17008                             cls: 'ui-resizable-handle ui-resizable-e',
17009                             html : '&nbsp;&nbsp;&nbsp'
17010                         }
17011
17012                     ]
17013                 };
17014
17015                 var ctr = _this.el.select('.fc-event-container',true).first();
17016                 var cg = ctr.createChild(cfg);
17017
17018                 var sbox = c.select('.fc-day-content',true).first().getBox();
17019                 var ebox = c.select('.fc-day-content',true).first().getBox();
17020                 //Roo.log(cg);
17021                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17022                 cg.setWidth(ebox.right - sbox.x -2);
17023
17024                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17025                 
17026             }
17027             
17028         });
17029         
17030         
17031         
17032     },
17033     
17034     onEventEnter: function (e, el,event,d) {
17035         this.fireEvent('evententer', this, el, event);
17036     },
17037     
17038     onEventLeave: function (e, el,event,d) {
17039         this.fireEvent('eventleave', this, el, event);
17040     },
17041     
17042     onEventClick: function (e, el,event,d) {
17043         this.fireEvent('eventclick', this, el, event);
17044     },
17045     
17046     onMonthChange: function () {
17047         this.store.load();
17048     },
17049     
17050     onMoreEventClick: function(e, el, more)
17051     {
17052         var _this = this;
17053         
17054         this.calpopover.placement = 'right';
17055         this.calpopover.setTitle('More');
17056         
17057         this.calpopover.setContent('');
17058         
17059         var ctr = this.calpopover.el.select('.popover-content', true).first();
17060         
17061         Roo.each(more, function(m){
17062             var cfg = {
17063                 cls : 'fc-event-hori fc-event-draggable',
17064                 html : m.title
17065             };
17066             var cg = ctr.createChild(cfg);
17067             
17068             cg.on('click', _this.onEventClick, _this, m);
17069         });
17070         
17071         this.calpopover.show(el);
17072         
17073         
17074     },
17075     
17076     onLoad: function () 
17077     {   
17078         this.calevents = [];
17079         var cal = this;
17080         
17081         if(this.store.getCount() > 0){
17082             this.store.data.each(function(d){
17083                cal.addItem({
17084                     id : d.data.id,
17085                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17086                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17087                     time : d.data.start_time,
17088                     title : d.data.title,
17089                     description : d.data.description,
17090                     venue : d.data.venue
17091                 });
17092             });
17093         }
17094         
17095         this.renderEvents();
17096         
17097         if(this.calevents.length && this.loadMask){
17098             this.maskEl.hide();
17099         }
17100     },
17101     
17102     onBeforeLoad: function()
17103     {
17104         this.clearEvents();
17105         if(this.loadMask){
17106             this.maskEl.show();
17107         }
17108     }
17109 });
17110
17111  
17112  /*
17113  * - LGPL
17114  *
17115  * element
17116  * 
17117  */
17118
17119 /**
17120  * @class Roo.bootstrap.Popover
17121  * @extends Roo.bootstrap.Component
17122  * Bootstrap Popover class
17123  * @cfg {String} html contents of the popover   (or false to use children..)
17124  * @cfg {String} title of popover (or false to hide)
17125  * @cfg {String} placement how it is placed
17126  * @cfg {String} trigger click || hover (or false to trigger manually)
17127  * @cfg {String} over what (parent or false to trigger manually.)
17128  * @cfg {Number} delay - delay before showing
17129  
17130  * @constructor
17131  * Create a new Popover
17132  * @param {Object} config The config object
17133  */
17134
17135 Roo.bootstrap.Popover = function(config){
17136     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17137     
17138     this.addEvents({
17139         // raw events
17140          /**
17141          * @event show
17142          * After the popover show
17143          * 
17144          * @param {Roo.bootstrap.Popover} this
17145          */
17146         "show" : true,
17147         /**
17148          * @event hide
17149          * After the popover hide
17150          * 
17151          * @param {Roo.bootstrap.Popover} this
17152          */
17153         "hide" : true
17154     });
17155 };
17156
17157 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17158     
17159     title: 'Fill in a title',
17160     html: false,
17161     
17162     placement : 'right',
17163     trigger : 'hover', // hover
17164     
17165     delay : 0,
17166     
17167     over: 'parent',
17168     
17169     can_build_overlaid : false,
17170     
17171     getChildContainer : function()
17172     {
17173         return this.el.select('.popover-content',true).first();
17174     },
17175     
17176     getAutoCreate : function(){
17177          
17178         var cfg = {
17179            cls : 'popover roo-dynamic',
17180            style: 'display:block',
17181            cn : [
17182                 {
17183                     cls : 'arrow'
17184                 },
17185                 {
17186                     cls : 'popover-inner',
17187                     cn : [
17188                         {
17189                             tag: 'h3',
17190                             cls: 'popover-title',
17191                             html : this.title
17192                         },
17193                         {
17194                             cls : 'popover-content',
17195                             html : this.html
17196                         }
17197                     ]
17198                     
17199                 }
17200            ]
17201         };
17202         
17203         return cfg;
17204     },
17205     setTitle: function(str)
17206     {
17207         this.title = str;
17208         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17209     },
17210     setContent: function(str)
17211     {
17212         this.html = str;
17213         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17214     },
17215     // as it get's added to the bottom of the page.
17216     onRender : function(ct, position)
17217     {
17218         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17219         if(!this.el){
17220             var cfg = Roo.apply({},  this.getAutoCreate());
17221             cfg.id = Roo.id();
17222             
17223             if (this.cls) {
17224                 cfg.cls += ' ' + this.cls;
17225             }
17226             if (this.style) {
17227                 cfg.style = this.style;
17228             }
17229             //Roo.log("adding to ");
17230             this.el = Roo.get(document.body).createChild(cfg, position);
17231 //            Roo.log(this.el);
17232         }
17233         this.initEvents();
17234     },
17235     
17236     initEvents : function()
17237     {
17238         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17239         this.el.enableDisplayMode('block');
17240         this.el.hide();
17241         if (this.over === false) {
17242             return; 
17243         }
17244         if (this.triggers === false) {
17245             return;
17246         }
17247         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17248         var triggers = this.trigger ? this.trigger.split(' ') : [];
17249         Roo.each(triggers, function(trigger) {
17250         
17251             if (trigger == 'click') {
17252                 on_el.on('click', this.toggle, this);
17253             } else if (trigger != 'manual') {
17254                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17255                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17256       
17257                 on_el.on(eventIn  ,this.enter, this);
17258                 on_el.on(eventOut, this.leave, this);
17259             }
17260         }, this);
17261         
17262     },
17263     
17264     
17265     // private
17266     timeout : null,
17267     hoverState : null,
17268     
17269     toggle : function () {
17270         this.hoverState == 'in' ? this.leave() : this.enter();
17271     },
17272     
17273     enter : function () {
17274         
17275         clearTimeout(this.timeout);
17276     
17277         this.hoverState = 'in';
17278     
17279         if (!this.delay || !this.delay.show) {
17280             this.show();
17281             return;
17282         }
17283         var _t = this;
17284         this.timeout = setTimeout(function () {
17285             if (_t.hoverState == 'in') {
17286                 _t.show();
17287             }
17288         }, this.delay.show)
17289     },
17290     
17291     leave : function() {
17292         clearTimeout(this.timeout);
17293     
17294         this.hoverState = 'out';
17295     
17296         if (!this.delay || !this.delay.hide) {
17297             this.hide();
17298             return;
17299         }
17300         var _t = this;
17301         this.timeout = setTimeout(function () {
17302             if (_t.hoverState == 'out') {
17303                 _t.hide();
17304             }
17305         }, this.delay.hide)
17306     },
17307     
17308     show : function (on_el)
17309     {
17310         if (!on_el) {
17311             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17312         }
17313         
17314         // set content.
17315         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17316         if (this.html !== false) {
17317             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17318         }
17319         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17320         if (!this.title.length) {
17321             this.el.select('.popover-title',true).hide();
17322         }
17323         
17324         var placement = typeof this.placement == 'function' ?
17325             this.placement.call(this, this.el, on_el) :
17326             this.placement;
17327             
17328         var autoToken = /\s?auto?\s?/i;
17329         var autoPlace = autoToken.test(placement);
17330         if (autoPlace) {
17331             placement = placement.replace(autoToken, '') || 'top';
17332         }
17333         
17334         //this.el.detach()
17335         //this.el.setXY([0,0]);
17336         this.el.show();
17337         this.el.dom.style.display='block';
17338         this.el.addClass(placement);
17339         
17340         //this.el.appendTo(on_el);
17341         
17342         var p = this.getPosition();
17343         var box = this.el.getBox();
17344         
17345         if (autoPlace) {
17346             // fixme..
17347         }
17348         var align = Roo.bootstrap.Popover.alignment[placement];
17349         
17350 //        Roo.log(align);
17351         this.el.alignTo(on_el, align[0],align[1]);
17352         //var arrow = this.el.select('.arrow',true).first();
17353         //arrow.set(align[2], 
17354         
17355         this.el.addClass('in');
17356         
17357         
17358         if (this.el.hasClass('fade')) {
17359             // fade it?
17360         }
17361         
17362         this.hoverState = 'in';
17363         
17364         this.fireEvent('show', this);
17365         
17366     },
17367     hide : function()
17368     {
17369         this.el.setXY([0,0]);
17370         this.el.removeClass('in');
17371         this.el.hide();
17372         this.hoverState = null;
17373         
17374         this.fireEvent('hide', this);
17375     }
17376     
17377 });
17378
17379 Roo.bootstrap.Popover.alignment = {
17380     'left' : ['r-l', [-10,0], 'right'],
17381     'right' : ['l-r', [10,0], 'left'],
17382     'bottom' : ['t-b', [0,10], 'top'],
17383     'top' : [ 'b-t', [0,-10], 'bottom']
17384 };
17385
17386  /*
17387  * - LGPL
17388  *
17389  * Progress
17390  * 
17391  */
17392
17393 /**
17394  * @class Roo.bootstrap.Progress
17395  * @extends Roo.bootstrap.Component
17396  * Bootstrap Progress class
17397  * @cfg {Boolean} striped striped of the progress bar
17398  * @cfg {Boolean} active animated of the progress bar
17399  * 
17400  * 
17401  * @constructor
17402  * Create a new Progress
17403  * @param {Object} config The config object
17404  */
17405
17406 Roo.bootstrap.Progress = function(config){
17407     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17408 };
17409
17410 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17411     
17412     striped : false,
17413     active: false,
17414     
17415     getAutoCreate : function(){
17416         var cfg = {
17417             tag: 'div',
17418             cls: 'progress'
17419         };
17420         
17421         
17422         if(this.striped){
17423             cfg.cls += ' progress-striped';
17424         }
17425       
17426         if(this.active){
17427             cfg.cls += ' active';
17428         }
17429         
17430         
17431         return cfg;
17432     }
17433    
17434 });
17435
17436  
17437
17438  /*
17439  * - LGPL
17440  *
17441  * ProgressBar
17442  * 
17443  */
17444
17445 /**
17446  * @class Roo.bootstrap.ProgressBar
17447  * @extends Roo.bootstrap.Component
17448  * Bootstrap ProgressBar class
17449  * @cfg {Number} aria_valuenow aria-value now
17450  * @cfg {Number} aria_valuemin aria-value min
17451  * @cfg {Number} aria_valuemax aria-value max
17452  * @cfg {String} label label for the progress bar
17453  * @cfg {String} panel (success | info | warning | danger )
17454  * @cfg {String} role role of the progress bar
17455  * @cfg {String} sr_only text
17456  * 
17457  * 
17458  * @constructor
17459  * Create a new ProgressBar
17460  * @param {Object} config The config object
17461  */
17462
17463 Roo.bootstrap.ProgressBar = function(config){
17464     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17465 };
17466
17467 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17468     
17469     aria_valuenow : 0,
17470     aria_valuemin : 0,
17471     aria_valuemax : 100,
17472     label : false,
17473     panel : false,
17474     role : false,
17475     sr_only: false,
17476     
17477     getAutoCreate : function()
17478     {
17479         
17480         var cfg = {
17481             tag: 'div',
17482             cls: 'progress-bar',
17483             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17484         };
17485         
17486         if(this.sr_only){
17487             cfg.cn = {
17488                 tag: 'span',
17489                 cls: 'sr-only',
17490                 html: this.sr_only
17491             }
17492         }
17493         
17494         if(this.role){
17495             cfg.role = this.role;
17496         }
17497         
17498         if(this.aria_valuenow){
17499             cfg['aria-valuenow'] = this.aria_valuenow;
17500         }
17501         
17502         if(this.aria_valuemin){
17503             cfg['aria-valuemin'] = this.aria_valuemin;
17504         }
17505         
17506         if(this.aria_valuemax){
17507             cfg['aria-valuemax'] = this.aria_valuemax;
17508         }
17509         
17510         if(this.label && !this.sr_only){
17511             cfg.html = this.label;
17512         }
17513         
17514         if(this.panel){
17515             cfg.cls += ' progress-bar-' + this.panel;
17516         }
17517         
17518         return cfg;
17519     },
17520     
17521     update : function(aria_valuenow)
17522     {
17523         this.aria_valuenow = aria_valuenow;
17524         
17525         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17526     }
17527    
17528 });
17529
17530  
17531
17532  /*
17533  * - LGPL
17534  *
17535  * column
17536  * 
17537  */
17538
17539 /**
17540  * @class Roo.bootstrap.TabGroup
17541  * @extends Roo.bootstrap.Column
17542  * Bootstrap Column class
17543  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17544  * @cfg {Boolean} carousel true to make the group behave like a carousel
17545  * @cfg {Boolean} bullets show bullets for the panels
17546  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17547  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17548  * @cfg {Boolean} showarrow (true|false) show arrow default true
17549  * 
17550  * @constructor
17551  * Create a new TabGroup
17552  * @param {Object} config The config object
17553  */
17554
17555 Roo.bootstrap.TabGroup = function(config){
17556     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17557     if (!this.navId) {
17558         this.navId = Roo.id();
17559     }
17560     this.tabs = [];
17561     Roo.bootstrap.TabGroup.register(this);
17562     
17563 };
17564
17565 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17566     
17567     carousel : false,
17568     transition : false,
17569     bullets : 0,
17570     timer : 0,
17571     autoslide : false,
17572     slideFn : false,
17573     slideOnTouch : false,
17574     showarrow : true,
17575     
17576     getAutoCreate : function()
17577     {
17578         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17579         
17580         cfg.cls += ' tab-content';
17581         
17582         if (this.carousel) {
17583             cfg.cls += ' carousel slide';
17584             
17585             cfg.cn = [{
17586                cls : 'carousel-inner',
17587                cn : []
17588             }];
17589         
17590             if(this.bullets  && !Roo.isTouch){
17591                 
17592                 var bullets = {
17593                     cls : 'carousel-bullets',
17594                     cn : []
17595                 };
17596                
17597                 if(this.bullets_cls){
17598                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17599                 }
17600                 
17601                 bullets.cn.push({
17602                     cls : 'clear'
17603                 });
17604                 
17605                 cfg.cn[0].cn.push(bullets);
17606             }
17607             
17608             if(this.showarrow){
17609                 cfg.cn[0].cn.push({
17610                     tag : 'div',
17611                     class : 'carousel-arrow',
17612                     cn : [
17613                         {
17614                             tag : 'div',
17615                             class : 'carousel-prev',
17616                             cn : [
17617                                 {
17618                                     tag : 'i',
17619                                     class : 'fa fa-chevron-left'
17620                                 }
17621                             ]
17622                         },
17623                         {
17624                             tag : 'div',
17625                             class : 'carousel-next',
17626                             cn : [
17627                                 {
17628                                     tag : 'i',
17629                                     class : 'fa fa-chevron-right'
17630                                 }
17631                             ]
17632                         }
17633                     ]
17634                 });
17635             }
17636             
17637         }
17638         
17639         return cfg;
17640     },
17641     
17642     initEvents:  function()
17643     {
17644 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17645 //            this.el.on("touchstart", this.onTouchStart, this);
17646 //        }
17647         
17648         if(this.autoslide){
17649             var _this = this;
17650             
17651             this.slideFn = window.setInterval(function() {
17652                 _this.showPanelNext();
17653             }, this.timer);
17654         }
17655         
17656         if(this.showarrow){
17657             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17658             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17659         }
17660         
17661         
17662     },
17663     
17664 //    onTouchStart : function(e, el, o)
17665 //    {
17666 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17667 //            return;
17668 //        }
17669 //        
17670 //        this.showPanelNext();
17671 //    },
17672     
17673     
17674     getChildContainer : function()
17675     {
17676         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17677     },
17678     
17679     /**
17680     * register a Navigation item
17681     * @param {Roo.bootstrap.NavItem} the navitem to add
17682     */
17683     register : function(item)
17684     {
17685         this.tabs.push( item);
17686         item.navId = this.navId; // not really needed..
17687         this.addBullet();
17688     
17689     },
17690     
17691     getActivePanel : function()
17692     {
17693         var r = false;
17694         Roo.each(this.tabs, function(t) {
17695             if (t.active) {
17696                 r = t;
17697                 return false;
17698             }
17699             return null;
17700         });
17701         return r;
17702         
17703     },
17704     getPanelByName : function(n)
17705     {
17706         var r = false;
17707         Roo.each(this.tabs, function(t) {
17708             if (t.tabId == n) {
17709                 r = t;
17710                 return false;
17711             }
17712             return null;
17713         });
17714         return r;
17715     },
17716     indexOfPanel : function(p)
17717     {
17718         var r = false;
17719         Roo.each(this.tabs, function(t,i) {
17720             if (t.tabId == p.tabId) {
17721                 r = i;
17722                 return false;
17723             }
17724             return null;
17725         });
17726         return r;
17727     },
17728     /**
17729      * show a specific panel
17730      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17731      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17732      */
17733     showPanel : function (pan)
17734     {
17735         if(this.transition || typeof(pan) == 'undefined'){
17736             Roo.log("waiting for the transitionend");
17737             return;
17738         }
17739         
17740         if (typeof(pan) == 'number') {
17741             pan = this.tabs[pan];
17742         }
17743         
17744         if (typeof(pan) == 'string') {
17745             pan = this.getPanelByName(pan);
17746         }
17747         
17748         var cur = this.getActivePanel();
17749         
17750         if(!pan || !cur){
17751             Roo.log('pan or acitve pan is undefined');
17752             return false;
17753         }
17754         
17755         if (pan.tabId == this.getActivePanel().tabId) {
17756             return true;
17757         }
17758         
17759         if (false === cur.fireEvent('beforedeactivate')) {
17760             return false;
17761         }
17762         
17763         if(this.bullets > 0 && !Roo.isTouch){
17764             this.setActiveBullet(this.indexOfPanel(pan));
17765         }
17766         
17767         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17768             
17769             this.transition = true;
17770             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17771             var lr = dir == 'next' ? 'left' : 'right';
17772             pan.el.addClass(dir); // or prev
17773             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17774             cur.el.addClass(lr); // or right
17775             pan.el.addClass(lr);
17776             
17777             var _this = this;
17778             cur.el.on('transitionend', function() {
17779                 Roo.log("trans end?");
17780                 
17781                 pan.el.removeClass([lr,dir]);
17782                 pan.setActive(true);
17783                 
17784                 cur.el.removeClass([lr]);
17785                 cur.setActive(false);
17786                 
17787                 _this.transition = false;
17788                 
17789             }, this, { single:  true } );
17790             
17791             return true;
17792         }
17793         
17794         cur.setActive(false);
17795         pan.setActive(true);
17796         
17797         return true;
17798         
17799     },
17800     showPanelNext : function()
17801     {
17802         var i = this.indexOfPanel(this.getActivePanel());
17803         
17804         if (i >= this.tabs.length - 1 && !this.autoslide) {
17805             return;
17806         }
17807         
17808         if (i >= this.tabs.length - 1 && this.autoslide) {
17809             i = -1;
17810         }
17811         
17812         this.showPanel(this.tabs[i+1]);
17813     },
17814     
17815     showPanelPrev : function()
17816     {
17817         var i = this.indexOfPanel(this.getActivePanel());
17818         
17819         if (i  < 1 && !this.autoslide) {
17820             return;
17821         }
17822         
17823         if (i < 1 && this.autoslide) {
17824             i = this.tabs.length;
17825         }
17826         
17827         this.showPanel(this.tabs[i-1]);
17828     },
17829     
17830     
17831     addBullet: function()
17832     {
17833         if(!this.bullets || Roo.isTouch){
17834             return;
17835         }
17836         var ctr = this.el.select('.carousel-bullets',true).first();
17837         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17838         var bullet = ctr.createChild({
17839             cls : 'bullet bullet-' + i
17840         },ctr.dom.lastChild);
17841         
17842         
17843         var _this = this;
17844         
17845         bullet.on('click', (function(e, el, o, ii, t){
17846
17847             e.preventDefault();
17848
17849             this.showPanel(ii);
17850
17851             if(this.autoslide && this.slideFn){
17852                 clearInterval(this.slideFn);
17853                 this.slideFn = window.setInterval(function() {
17854                     _this.showPanelNext();
17855                 }, this.timer);
17856             }
17857
17858         }).createDelegate(this, [i, bullet], true));
17859                 
17860         
17861     },
17862      
17863     setActiveBullet : function(i)
17864     {
17865         if(Roo.isTouch){
17866             return;
17867         }
17868         
17869         Roo.each(this.el.select('.bullet', true).elements, function(el){
17870             el.removeClass('selected');
17871         });
17872
17873         var bullet = this.el.select('.bullet-' + i, true).first();
17874         
17875         if(!bullet){
17876             return;
17877         }
17878         
17879         bullet.addClass('selected');
17880     }
17881     
17882     
17883   
17884 });
17885
17886  
17887
17888  
17889  
17890 Roo.apply(Roo.bootstrap.TabGroup, {
17891     
17892     groups: {},
17893      /**
17894     * register a Navigation Group
17895     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17896     */
17897     register : function(navgrp)
17898     {
17899         this.groups[navgrp.navId] = navgrp;
17900         
17901     },
17902     /**
17903     * fetch a Navigation Group based on the navigation ID
17904     * if one does not exist , it will get created.
17905     * @param {string} the navgroup to add
17906     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17907     */
17908     get: function(navId) {
17909         if (typeof(this.groups[navId]) == 'undefined') {
17910             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17911         }
17912         return this.groups[navId] ;
17913     }
17914     
17915     
17916     
17917 });
17918
17919  /*
17920  * - LGPL
17921  *
17922  * TabPanel
17923  * 
17924  */
17925
17926 /**
17927  * @class Roo.bootstrap.TabPanel
17928  * @extends Roo.bootstrap.Component
17929  * Bootstrap TabPanel class
17930  * @cfg {Boolean} active panel active
17931  * @cfg {String} html panel content
17932  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17933  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17934  * @cfg {String} href click to link..
17935  * 
17936  * 
17937  * @constructor
17938  * Create a new TabPanel
17939  * @param {Object} config The config object
17940  */
17941
17942 Roo.bootstrap.TabPanel = function(config){
17943     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17944     this.addEvents({
17945         /**
17946              * @event changed
17947              * Fires when the active status changes
17948              * @param {Roo.bootstrap.TabPanel} this
17949              * @param {Boolean} state the new state
17950             
17951          */
17952         'changed': true,
17953         /**
17954              * @event beforedeactivate
17955              * Fires before a tab is de-activated - can be used to do validation on a form.
17956              * @param {Roo.bootstrap.TabPanel} this
17957              * @return {Boolean} false if there is an error
17958             
17959          */
17960         'beforedeactivate': true
17961      });
17962     
17963     this.tabId = this.tabId || Roo.id();
17964   
17965 };
17966
17967 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17968     
17969     active: false,
17970     html: false,
17971     tabId: false,
17972     navId : false,
17973     href : '',
17974     
17975     getAutoCreate : function(){
17976         var cfg = {
17977             tag: 'div',
17978             // item is needed for carousel - not sure if it has any effect otherwise
17979             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17980             html: this.html || ''
17981         };
17982         
17983         if(this.active){
17984             cfg.cls += ' active';
17985         }
17986         
17987         if(this.tabId){
17988             cfg.tabId = this.tabId;
17989         }
17990         
17991         
17992         return cfg;
17993     },
17994     
17995     initEvents:  function()
17996     {
17997         var p = this.parent();
17998         
17999         this.navId = this.navId || p.navId;
18000         
18001         if (typeof(this.navId) != 'undefined') {
18002             // not really needed.. but just in case.. parent should be a NavGroup.
18003             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18004             
18005             tg.register(this);
18006             
18007             var i = tg.tabs.length - 1;
18008             
18009             if(this.active && tg.bullets > 0 && i < tg.bullets){
18010                 tg.setActiveBullet(i);
18011             }
18012         }
18013         
18014         this.el.on('click', this.onClick, this);
18015         
18016         if(Roo.isTouch){
18017             this.el.on("touchstart", this.onTouchStart, this);
18018             this.el.on("touchmove", this.onTouchMove, this);
18019             this.el.on("touchend", this.onTouchEnd, this);
18020         }
18021         
18022     },
18023     
18024     onRender : function(ct, position)
18025     {
18026         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18027     },
18028     
18029     setActive : function(state)
18030     {
18031         Roo.log("panel - set active " + this.tabId + "=" + state);
18032         
18033         this.active = state;
18034         if (!state) {
18035             this.el.removeClass('active');
18036             
18037         } else  if (!this.el.hasClass('active')) {
18038             this.el.addClass('active');
18039         }
18040         
18041         this.fireEvent('changed', this, state);
18042     },
18043     
18044     onClick : function(e)
18045     {
18046         e.preventDefault();
18047         
18048         if(!this.href.length){
18049             return;
18050         }
18051         
18052         window.location.href = this.href;
18053     },
18054     
18055     startX : 0,
18056     startY : 0,
18057     endX : 0,
18058     endY : 0,
18059     swiping : false,
18060     
18061     onTouchStart : function(e)
18062     {
18063         this.swiping = false;
18064         
18065         this.startX = e.browserEvent.touches[0].clientX;
18066         this.startY = e.browserEvent.touches[0].clientY;
18067     },
18068     
18069     onTouchMove : function(e)
18070     {
18071         this.swiping = true;
18072         
18073         this.endX = e.browserEvent.touches[0].clientX;
18074         this.endY = e.browserEvent.touches[0].clientY;
18075     },
18076     
18077     onTouchEnd : function(e)
18078     {
18079         if(!this.swiping){
18080             this.onClick(e);
18081             return;
18082         }
18083         
18084         var tabGroup = this.parent();
18085         
18086         if(this.endX > this.startX){ // swiping right
18087             tabGroup.showPanelPrev();
18088             return;
18089         }
18090         
18091         if(this.startX > this.endX){ // swiping left
18092             tabGroup.showPanelNext();
18093             return;
18094         }
18095     }
18096     
18097     
18098 });
18099  
18100
18101  
18102
18103  /*
18104  * - LGPL
18105  *
18106  * DateField
18107  * 
18108  */
18109
18110 /**
18111  * @class Roo.bootstrap.DateField
18112  * @extends Roo.bootstrap.Input
18113  * Bootstrap DateField class
18114  * @cfg {Number} weekStart default 0
18115  * @cfg {String} viewMode default empty, (months|years)
18116  * @cfg {String} minViewMode default empty, (months|years)
18117  * @cfg {Number} startDate default -Infinity
18118  * @cfg {Number} endDate default Infinity
18119  * @cfg {Boolean} todayHighlight default false
18120  * @cfg {Boolean} todayBtn default false
18121  * @cfg {Boolean} calendarWeeks default false
18122  * @cfg {Object} daysOfWeekDisabled default empty
18123  * @cfg {Boolean} singleMode default false (true | false)
18124  * 
18125  * @cfg {Boolean} keyboardNavigation default true
18126  * @cfg {String} language default en
18127  * 
18128  * @constructor
18129  * Create a new DateField
18130  * @param {Object} config The config object
18131  */
18132
18133 Roo.bootstrap.DateField = function(config){
18134     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18135      this.addEvents({
18136             /**
18137              * @event show
18138              * Fires when this field show.
18139              * @param {Roo.bootstrap.DateField} this
18140              * @param {Mixed} date The date value
18141              */
18142             show : true,
18143             /**
18144              * @event show
18145              * Fires when this field hide.
18146              * @param {Roo.bootstrap.DateField} this
18147              * @param {Mixed} date The date value
18148              */
18149             hide : true,
18150             /**
18151              * @event select
18152              * Fires when select a date.
18153              * @param {Roo.bootstrap.DateField} this
18154              * @param {Mixed} date The date value
18155              */
18156             select : true,
18157             /**
18158              * @event beforeselect
18159              * Fires when before select a date.
18160              * @param {Roo.bootstrap.DateField} this
18161              * @param {Mixed} date The date value
18162              */
18163             beforeselect : true
18164         });
18165 };
18166
18167 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18168     
18169     /**
18170      * @cfg {String} format
18171      * The default date format string which can be overriden for localization support.  The format must be
18172      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18173      */
18174     format : "m/d/y",
18175     /**
18176      * @cfg {String} altFormats
18177      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18178      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18179      */
18180     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18181     
18182     weekStart : 0,
18183     
18184     viewMode : '',
18185     
18186     minViewMode : '',
18187     
18188     todayHighlight : false,
18189     
18190     todayBtn: false,
18191     
18192     language: 'en',
18193     
18194     keyboardNavigation: true,
18195     
18196     calendarWeeks: false,
18197     
18198     startDate: -Infinity,
18199     
18200     endDate: Infinity,
18201     
18202     daysOfWeekDisabled: [],
18203     
18204     _events: [],
18205     
18206     singleMode : false,
18207     
18208     UTCDate: function()
18209     {
18210         return new Date(Date.UTC.apply(Date, arguments));
18211     },
18212     
18213     UTCToday: function()
18214     {
18215         var today = new Date();
18216         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18217     },
18218     
18219     getDate: function() {
18220             var d = this.getUTCDate();
18221             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18222     },
18223     
18224     getUTCDate: function() {
18225             return this.date;
18226     },
18227     
18228     setDate: function(d) {
18229             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18230     },
18231     
18232     setUTCDate: function(d) {
18233             this.date = d;
18234             this.setValue(this.formatDate(this.date));
18235     },
18236         
18237     onRender: function(ct, position)
18238     {
18239         
18240         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18241         
18242         this.language = this.language || 'en';
18243         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18244         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18245         
18246         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18247         this.format = this.format || 'm/d/y';
18248         this.isInline = false;
18249         this.isInput = true;
18250         this.component = this.el.select('.add-on', true).first() || false;
18251         this.component = (this.component && this.component.length === 0) ? false : this.component;
18252         this.hasInput = this.component && this.inputEl().length;
18253         
18254         if (typeof(this.minViewMode === 'string')) {
18255             switch (this.minViewMode) {
18256                 case 'months':
18257                     this.minViewMode = 1;
18258                     break;
18259                 case 'years':
18260                     this.minViewMode = 2;
18261                     break;
18262                 default:
18263                     this.minViewMode = 0;
18264                     break;
18265             }
18266         }
18267         
18268         if (typeof(this.viewMode === 'string')) {
18269             switch (this.viewMode) {
18270                 case 'months':
18271                     this.viewMode = 1;
18272                     break;
18273                 case 'years':
18274                     this.viewMode = 2;
18275                     break;
18276                 default:
18277                     this.viewMode = 0;
18278                     break;
18279             }
18280         }
18281                 
18282         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18283         
18284 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18285         
18286         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18287         
18288         this.picker().on('mousedown', this.onMousedown, this);
18289         this.picker().on('click', this.onClick, this);
18290         
18291         this.picker().addClass('datepicker-dropdown');
18292         
18293         this.startViewMode = this.viewMode;
18294         
18295         if(this.singleMode){
18296             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18297                 v.setVisibilityMode(Roo.Element.DISPLAY);
18298                 v.hide();
18299             });
18300             
18301             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18302                 v.setStyle('width', '189px');
18303             });
18304         }
18305         
18306         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18307             if(!this.calendarWeeks){
18308                 v.remove();
18309                 return;
18310             }
18311             
18312             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18313             v.attr('colspan', function(i, val){
18314                 return parseInt(val) + 1;
18315             });
18316         });
18317                         
18318         
18319         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18320         
18321         this.setStartDate(this.startDate);
18322         this.setEndDate(this.endDate);
18323         
18324         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18325         
18326         this.fillDow();
18327         this.fillMonths();
18328         this.update();
18329         this.showMode();
18330         
18331         if(this.isInline) {
18332             this.show();
18333         }
18334     },
18335     
18336     picker : function()
18337     {
18338         return this.pickerEl;
18339 //        return this.el.select('.datepicker', true).first();
18340     },
18341     
18342     fillDow: function()
18343     {
18344         var dowCnt = this.weekStart;
18345         
18346         var dow = {
18347             tag: 'tr',
18348             cn: [
18349                 
18350             ]
18351         };
18352         
18353         if(this.calendarWeeks){
18354             dow.cn.push({
18355                 tag: 'th',
18356                 cls: 'cw',
18357                 html: '&nbsp;'
18358             })
18359         }
18360         
18361         while (dowCnt < this.weekStart + 7) {
18362             dow.cn.push({
18363                 tag: 'th',
18364                 cls: 'dow',
18365                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18366             });
18367         }
18368         
18369         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18370     },
18371     
18372     fillMonths: function()
18373     {    
18374         var i = 0;
18375         var months = this.picker().select('>.datepicker-months td', true).first();
18376         
18377         months.dom.innerHTML = '';
18378         
18379         while (i < 12) {
18380             var month = {
18381                 tag: 'span',
18382                 cls: 'month',
18383                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18384             };
18385             
18386             months.createChild(month);
18387         }
18388         
18389     },
18390     
18391     update: function()
18392     {
18393         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;
18394         
18395         if (this.date < this.startDate) {
18396             this.viewDate = new Date(this.startDate);
18397         } else if (this.date > this.endDate) {
18398             this.viewDate = new Date(this.endDate);
18399         } else {
18400             this.viewDate = new Date(this.date);
18401         }
18402         
18403         this.fill();
18404     },
18405     
18406     fill: function() 
18407     {
18408         var d = new Date(this.viewDate),
18409                 year = d.getUTCFullYear(),
18410                 month = d.getUTCMonth(),
18411                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18412                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18413                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18414                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18415                 currentDate = this.date && this.date.valueOf(),
18416                 today = this.UTCToday();
18417         
18418         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18419         
18420 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18421         
18422 //        this.picker.select('>tfoot th.today').
18423 //                                              .text(dates[this.language].today)
18424 //                                              .toggle(this.todayBtn !== false);
18425     
18426         this.updateNavArrows();
18427         this.fillMonths();
18428                                                 
18429         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18430         
18431         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18432          
18433         prevMonth.setUTCDate(day);
18434         
18435         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18436         
18437         var nextMonth = new Date(prevMonth);
18438         
18439         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18440         
18441         nextMonth = nextMonth.valueOf();
18442         
18443         var fillMonths = false;
18444         
18445         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18446         
18447         while(prevMonth.valueOf() < nextMonth) {
18448             var clsName = '';
18449             
18450             if (prevMonth.getUTCDay() === this.weekStart) {
18451                 if(fillMonths){
18452                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18453                 }
18454                     
18455                 fillMonths = {
18456                     tag: 'tr',
18457                     cn: []
18458                 };
18459                 
18460                 if(this.calendarWeeks){
18461                     // ISO 8601: First week contains first thursday.
18462                     // ISO also states week starts on Monday, but we can be more abstract here.
18463                     var
18464                     // Start of current week: based on weekstart/current date
18465                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18466                     // Thursday of this week
18467                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18468                     // First Thursday of year, year from thursday
18469                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18470                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18471                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18472                     
18473                     fillMonths.cn.push({
18474                         tag: 'td',
18475                         cls: 'cw',
18476                         html: calWeek
18477                     });
18478                 }
18479             }
18480             
18481             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18482                 clsName += ' old';
18483             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18484                 clsName += ' new';
18485             }
18486             if (this.todayHighlight &&
18487                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18488                 prevMonth.getUTCMonth() == today.getMonth() &&
18489                 prevMonth.getUTCDate() == today.getDate()) {
18490                 clsName += ' today';
18491             }
18492             
18493             if (currentDate && prevMonth.valueOf() === currentDate) {
18494                 clsName += ' active';
18495             }
18496             
18497             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18498                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18499                     clsName += ' disabled';
18500             }
18501             
18502             fillMonths.cn.push({
18503                 tag: 'td',
18504                 cls: 'day ' + clsName,
18505                 html: prevMonth.getDate()
18506             });
18507             
18508             prevMonth.setDate(prevMonth.getDate()+1);
18509         }
18510           
18511         var currentYear = this.date && this.date.getUTCFullYear();
18512         var currentMonth = this.date && this.date.getUTCMonth();
18513         
18514         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18515         
18516         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18517             v.removeClass('active');
18518             
18519             if(currentYear === year && k === currentMonth){
18520                 v.addClass('active');
18521             }
18522             
18523             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18524                 v.addClass('disabled');
18525             }
18526             
18527         });
18528         
18529         
18530         year = parseInt(year/10, 10) * 10;
18531         
18532         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18533         
18534         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18535         
18536         year -= 1;
18537         for (var i = -1; i < 11; i++) {
18538             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18539                 tag: 'span',
18540                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18541                 html: year
18542             });
18543             
18544             year += 1;
18545         }
18546     },
18547     
18548     showMode: function(dir) 
18549     {
18550         if (dir) {
18551             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18552         }
18553         
18554         Roo.each(this.picker().select('>div',true).elements, function(v){
18555             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18556             v.hide();
18557         });
18558         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18559     },
18560     
18561     place: function()
18562     {
18563         if(this.isInline) {
18564             return;
18565         }
18566         
18567         this.picker().removeClass(['bottom', 'top']);
18568         
18569         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18570             /*
18571              * place to the top of element!
18572              *
18573              */
18574             
18575             this.picker().addClass('top');
18576             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18577             
18578             return;
18579         }
18580         
18581         this.picker().addClass('bottom');
18582         
18583         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18584     },
18585     
18586     parseDate : function(value)
18587     {
18588         if(!value || value instanceof Date){
18589             return value;
18590         }
18591         var v = Date.parseDate(value, this.format);
18592         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18593             v = Date.parseDate(value, 'Y-m-d');
18594         }
18595         if(!v && this.altFormats){
18596             if(!this.altFormatsArray){
18597                 this.altFormatsArray = this.altFormats.split("|");
18598             }
18599             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18600                 v = Date.parseDate(value, this.altFormatsArray[i]);
18601             }
18602         }
18603         return v;
18604     },
18605     
18606     formatDate : function(date, fmt)
18607     {   
18608         return (!date || !(date instanceof Date)) ?
18609         date : date.dateFormat(fmt || this.format);
18610     },
18611     
18612     onFocus : function()
18613     {
18614         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18615         this.show();
18616     },
18617     
18618     onBlur : function()
18619     {
18620         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18621         
18622         var d = this.inputEl().getValue();
18623         
18624         this.setValue(d);
18625                 
18626         this.hide();
18627     },
18628     
18629     show : function()
18630     {
18631         this.picker().show();
18632         this.update();
18633         this.place();
18634         
18635         this.fireEvent('show', this, this.date);
18636     },
18637     
18638     hide : function()
18639     {
18640         if(this.isInline) {
18641             return;
18642         }
18643         this.picker().hide();
18644         this.viewMode = this.startViewMode;
18645         this.showMode();
18646         
18647         this.fireEvent('hide', this, this.date);
18648         
18649     },
18650     
18651     onMousedown: function(e)
18652     {
18653         e.stopPropagation();
18654         e.preventDefault();
18655     },
18656     
18657     keyup: function(e)
18658     {
18659         Roo.bootstrap.DateField.superclass.keyup.call(this);
18660         this.update();
18661     },
18662
18663     setValue: function(v)
18664     {
18665         if(this.fireEvent('beforeselect', this, v) !== false){
18666             var d = new Date(this.parseDate(v) ).clearTime();
18667         
18668             if(isNaN(d.getTime())){
18669                 this.date = this.viewDate = '';
18670                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18671                 return;
18672             }
18673
18674             v = this.formatDate(d);
18675
18676             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18677
18678             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18679
18680             this.update();
18681
18682             this.fireEvent('select', this, this.date);
18683         }
18684     },
18685     
18686     getValue: function()
18687     {
18688         return this.formatDate(this.date);
18689     },
18690     
18691     fireKey: function(e)
18692     {
18693         if (!this.picker().isVisible()){
18694             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18695                 this.show();
18696             }
18697             return;
18698         }
18699         
18700         var dateChanged = false,
18701         dir, day, month,
18702         newDate, newViewDate;
18703         
18704         switch(e.keyCode){
18705             case 27: // escape
18706                 this.hide();
18707                 e.preventDefault();
18708                 break;
18709             case 37: // left
18710             case 39: // right
18711                 if (!this.keyboardNavigation) {
18712                     break;
18713                 }
18714                 dir = e.keyCode == 37 ? -1 : 1;
18715                 
18716                 if (e.ctrlKey){
18717                     newDate = this.moveYear(this.date, dir);
18718                     newViewDate = this.moveYear(this.viewDate, dir);
18719                 } else if (e.shiftKey){
18720                     newDate = this.moveMonth(this.date, dir);
18721                     newViewDate = this.moveMonth(this.viewDate, dir);
18722                 } else {
18723                     newDate = new Date(this.date);
18724                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18725                     newViewDate = new Date(this.viewDate);
18726                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18727                 }
18728                 if (this.dateWithinRange(newDate)){
18729                     this.date = newDate;
18730                     this.viewDate = newViewDate;
18731                     this.setValue(this.formatDate(this.date));
18732 //                    this.update();
18733                     e.preventDefault();
18734                     dateChanged = true;
18735                 }
18736                 break;
18737             case 38: // up
18738             case 40: // down
18739                 if (!this.keyboardNavigation) {
18740                     break;
18741                 }
18742                 dir = e.keyCode == 38 ? -1 : 1;
18743                 if (e.ctrlKey){
18744                     newDate = this.moveYear(this.date, dir);
18745                     newViewDate = this.moveYear(this.viewDate, dir);
18746                 } else if (e.shiftKey){
18747                     newDate = this.moveMonth(this.date, dir);
18748                     newViewDate = this.moveMonth(this.viewDate, dir);
18749                 } else {
18750                     newDate = new Date(this.date);
18751                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18752                     newViewDate = new Date(this.viewDate);
18753                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18754                 }
18755                 if (this.dateWithinRange(newDate)){
18756                     this.date = newDate;
18757                     this.viewDate = newViewDate;
18758                     this.setValue(this.formatDate(this.date));
18759 //                    this.update();
18760                     e.preventDefault();
18761                     dateChanged = true;
18762                 }
18763                 break;
18764             case 13: // enter
18765                 this.setValue(this.formatDate(this.date));
18766                 this.hide();
18767                 e.preventDefault();
18768                 break;
18769             case 9: // tab
18770                 this.setValue(this.formatDate(this.date));
18771                 this.hide();
18772                 break;
18773             case 16: // shift
18774             case 17: // ctrl
18775             case 18: // alt
18776                 break;
18777             default :
18778                 this.hide();
18779                 
18780         }
18781     },
18782     
18783     
18784     onClick: function(e) 
18785     {
18786         e.stopPropagation();
18787         e.preventDefault();
18788         
18789         var target = e.getTarget();
18790         
18791         if(target.nodeName.toLowerCase() === 'i'){
18792             target = Roo.get(target).dom.parentNode;
18793         }
18794         
18795         var nodeName = target.nodeName;
18796         var className = target.className;
18797         var html = target.innerHTML;
18798         //Roo.log(nodeName);
18799         
18800         switch(nodeName.toLowerCase()) {
18801             case 'th':
18802                 switch(className) {
18803                     case 'switch':
18804                         this.showMode(1);
18805                         break;
18806                     case 'prev':
18807                     case 'next':
18808                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18809                         switch(this.viewMode){
18810                                 case 0:
18811                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18812                                         break;
18813                                 case 1:
18814                                 case 2:
18815                                         this.viewDate = this.moveYear(this.viewDate, dir);
18816                                         break;
18817                         }
18818                         this.fill();
18819                         break;
18820                     case 'today':
18821                         var date = new Date();
18822                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18823 //                        this.fill()
18824                         this.setValue(this.formatDate(this.date));
18825                         
18826                         this.hide();
18827                         break;
18828                 }
18829                 break;
18830             case 'span':
18831                 if (className.indexOf('disabled') < 0) {
18832                     this.viewDate.setUTCDate(1);
18833                     if (className.indexOf('month') > -1) {
18834                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18835                     } else {
18836                         var year = parseInt(html, 10) || 0;
18837                         this.viewDate.setUTCFullYear(year);
18838                         
18839                     }
18840                     
18841                     if(this.singleMode){
18842                         this.setValue(this.formatDate(this.viewDate));
18843                         this.hide();
18844                         return;
18845                     }
18846                     
18847                     this.showMode(-1);
18848                     this.fill();
18849                 }
18850                 break;
18851                 
18852             case 'td':
18853                 //Roo.log(className);
18854                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18855                     var day = parseInt(html, 10) || 1;
18856                     var year = this.viewDate.getUTCFullYear(),
18857                         month = this.viewDate.getUTCMonth();
18858
18859                     if (className.indexOf('old') > -1) {
18860                         if(month === 0 ){
18861                             month = 11;
18862                             year -= 1;
18863                         }else{
18864                             month -= 1;
18865                         }
18866                     } else if (className.indexOf('new') > -1) {
18867                         if (month == 11) {
18868                             month = 0;
18869                             year += 1;
18870                         } else {
18871                             month += 1;
18872                         }
18873                     }
18874                     //Roo.log([year,month,day]);
18875                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18876                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18877 //                    this.fill();
18878                     //Roo.log(this.formatDate(this.date));
18879                     this.setValue(this.formatDate(this.date));
18880                     this.hide();
18881                 }
18882                 break;
18883         }
18884     },
18885     
18886     setStartDate: function(startDate)
18887     {
18888         this.startDate = startDate || -Infinity;
18889         if (this.startDate !== -Infinity) {
18890             this.startDate = this.parseDate(this.startDate);
18891         }
18892         this.update();
18893         this.updateNavArrows();
18894     },
18895
18896     setEndDate: function(endDate)
18897     {
18898         this.endDate = endDate || Infinity;
18899         if (this.endDate !== Infinity) {
18900             this.endDate = this.parseDate(this.endDate);
18901         }
18902         this.update();
18903         this.updateNavArrows();
18904     },
18905     
18906     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18907     {
18908         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18909         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18910             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18911         }
18912         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18913             return parseInt(d, 10);
18914         });
18915         this.update();
18916         this.updateNavArrows();
18917     },
18918     
18919     updateNavArrows: function() 
18920     {
18921         if(this.singleMode){
18922             return;
18923         }
18924         
18925         var d = new Date(this.viewDate),
18926         year = d.getUTCFullYear(),
18927         month = d.getUTCMonth();
18928         
18929         Roo.each(this.picker().select('.prev', true).elements, function(v){
18930             v.show();
18931             switch (this.viewMode) {
18932                 case 0:
18933
18934                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18935                         v.hide();
18936                     }
18937                     break;
18938                 case 1:
18939                 case 2:
18940                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18941                         v.hide();
18942                     }
18943                     break;
18944             }
18945         });
18946         
18947         Roo.each(this.picker().select('.next', true).elements, function(v){
18948             v.show();
18949             switch (this.viewMode) {
18950                 case 0:
18951
18952                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18953                         v.hide();
18954                     }
18955                     break;
18956                 case 1:
18957                 case 2:
18958                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18959                         v.hide();
18960                     }
18961                     break;
18962             }
18963         })
18964     },
18965     
18966     moveMonth: function(date, dir)
18967     {
18968         if (!dir) {
18969             return date;
18970         }
18971         var new_date = new Date(date.valueOf()),
18972         day = new_date.getUTCDate(),
18973         month = new_date.getUTCMonth(),
18974         mag = Math.abs(dir),
18975         new_month, test;
18976         dir = dir > 0 ? 1 : -1;
18977         if (mag == 1){
18978             test = dir == -1
18979             // If going back one month, make sure month is not current month
18980             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18981             ? function(){
18982                 return new_date.getUTCMonth() == month;
18983             }
18984             // If going forward one month, make sure month is as expected
18985             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18986             : function(){
18987                 return new_date.getUTCMonth() != new_month;
18988             };
18989             new_month = month + dir;
18990             new_date.setUTCMonth(new_month);
18991             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18992             if (new_month < 0 || new_month > 11) {
18993                 new_month = (new_month + 12) % 12;
18994             }
18995         } else {
18996             // For magnitudes >1, move one month at a time...
18997             for (var i=0; i<mag; i++) {
18998                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18999                 new_date = this.moveMonth(new_date, dir);
19000             }
19001             // ...then reset the day, keeping it in the new month
19002             new_month = new_date.getUTCMonth();
19003             new_date.setUTCDate(day);
19004             test = function(){
19005                 return new_month != new_date.getUTCMonth();
19006             };
19007         }
19008         // Common date-resetting loop -- if date is beyond end of month, make it
19009         // end of month
19010         while (test()){
19011             new_date.setUTCDate(--day);
19012             new_date.setUTCMonth(new_month);
19013         }
19014         return new_date;
19015     },
19016
19017     moveYear: function(date, dir)
19018     {
19019         return this.moveMonth(date, dir*12);
19020     },
19021
19022     dateWithinRange: function(date)
19023     {
19024         return date >= this.startDate && date <= this.endDate;
19025     },
19026
19027     
19028     remove: function() 
19029     {
19030         this.picker().remove();
19031     },
19032     
19033     validateValue : function(value)
19034     {
19035         if(value.length < 1)  {
19036             if(this.allowBlank){
19037                 return true;
19038             }
19039             return false;
19040         }
19041         
19042         if(value.length < this.minLength){
19043             return false;
19044         }
19045         if(value.length > this.maxLength){
19046             return false;
19047         }
19048         if(this.vtype){
19049             var vt = Roo.form.VTypes;
19050             if(!vt[this.vtype](value, this)){
19051                 return false;
19052             }
19053         }
19054         if(typeof this.validator == "function"){
19055             var msg = this.validator(value);
19056             if(msg !== true){
19057                 return false;
19058             }
19059         }
19060         
19061         if(this.regex && !this.regex.test(value)){
19062             return false;
19063         }
19064         
19065         if(typeof(this.parseDate(value)) == 'undefined'){
19066             return false;
19067         }
19068         
19069         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19070             return false;
19071         }      
19072         
19073         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19074             return false;
19075         } 
19076         
19077         
19078         return true;
19079     }
19080    
19081 });
19082
19083 Roo.apply(Roo.bootstrap.DateField,  {
19084     
19085     head : {
19086         tag: 'thead',
19087         cn: [
19088         {
19089             tag: 'tr',
19090             cn: [
19091             {
19092                 tag: 'th',
19093                 cls: 'prev',
19094                 html: '<i class="fa fa-arrow-left"/>'
19095             },
19096             {
19097                 tag: 'th',
19098                 cls: 'switch',
19099                 colspan: '5'
19100             },
19101             {
19102                 tag: 'th',
19103                 cls: 'next',
19104                 html: '<i class="fa fa-arrow-right"/>'
19105             }
19106
19107             ]
19108         }
19109         ]
19110     },
19111     
19112     content : {
19113         tag: 'tbody',
19114         cn: [
19115         {
19116             tag: 'tr',
19117             cn: [
19118             {
19119                 tag: 'td',
19120                 colspan: '7'
19121             }
19122             ]
19123         }
19124         ]
19125     },
19126     
19127     footer : {
19128         tag: 'tfoot',
19129         cn: [
19130         {
19131             tag: 'tr',
19132             cn: [
19133             {
19134                 tag: 'th',
19135                 colspan: '7',
19136                 cls: 'today'
19137             }
19138                     
19139             ]
19140         }
19141         ]
19142     },
19143     
19144     dates:{
19145         en: {
19146             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19147             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19148             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19149             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19150             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19151             today: "Today"
19152         }
19153     },
19154     
19155     modes: [
19156     {
19157         clsName: 'days',
19158         navFnc: 'Month',
19159         navStep: 1
19160     },
19161     {
19162         clsName: 'months',
19163         navFnc: 'FullYear',
19164         navStep: 1
19165     },
19166     {
19167         clsName: 'years',
19168         navFnc: 'FullYear',
19169         navStep: 10
19170     }]
19171 });
19172
19173 Roo.apply(Roo.bootstrap.DateField,  {
19174   
19175     template : {
19176         tag: 'div',
19177         cls: 'datepicker dropdown-menu roo-dynamic',
19178         cn: [
19179         {
19180             tag: 'div',
19181             cls: 'datepicker-days',
19182             cn: [
19183             {
19184                 tag: 'table',
19185                 cls: 'table-condensed',
19186                 cn:[
19187                 Roo.bootstrap.DateField.head,
19188                 {
19189                     tag: 'tbody'
19190                 },
19191                 Roo.bootstrap.DateField.footer
19192                 ]
19193             }
19194             ]
19195         },
19196         {
19197             tag: 'div',
19198             cls: 'datepicker-months',
19199             cn: [
19200             {
19201                 tag: 'table',
19202                 cls: 'table-condensed',
19203                 cn:[
19204                 Roo.bootstrap.DateField.head,
19205                 Roo.bootstrap.DateField.content,
19206                 Roo.bootstrap.DateField.footer
19207                 ]
19208             }
19209             ]
19210         },
19211         {
19212             tag: 'div',
19213             cls: 'datepicker-years',
19214             cn: [
19215             {
19216                 tag: 'table',
19217                 cls: 'table-condensed',
19218                 cn:[
19219                 Roo.bootstrap.DateField.head,
19220                 Roo.bootstrap.DateField.content,
19221                 Roo.bootstrap.DateField.footer
19222                 ]
19223             }
19224             ]
19225         }
19226         ]
19227     }
19228 });
19229
19230  
19231
19232  /*
19233  * - LGPL
19234  *
19235  * TimeField
19236  * 
19237  */
19238
19239 /**
19240  * @class Roo.bootstrap.TimeField
19241  * @extends Roo.bootstrap.Input
19242  * Bootstrap DateField class
19243  * 
19244  * 
19245  * @constructor
19246  * Create a new TimeField
19247  * @param {Object} config The config object
19248  */
19249
19250 Roo.bootstrap.TimeField = function(config){
19251     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19252     this.addEvents({
19253             /**
19254              * @event show
19255              * Fires when this field show.
19256              * @param {Roo.bootstrap.DateField} thisthis
19257              * @param {Mixed} date The date value
19258              */
19259             show : true,
19260             /**
19261              * @event show
19262              * Fires when this field hide.
19263              * @param {Roo.bootstrap.DateField} this
19264              * @param {Mixed} date The date value
19265              */
19266             hide : true,
19267             /**
19268              * @event select
19269              * Fires when select a date.
19270              * @param {Roo.bootstrap.DateField} this
19271              * @param {Mixed} date The date value
19272              */
19273             select : true
19274         });
19275 };
19276
19277 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19278     
19279     /**
19280      * @cfg {String} format
19281      * The default time format string which can be overriden for localization support.  The format must be
19282      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19283      */
19284     format : "H:i",
19285        
19286     onRender: function(ct, position)
19287     {
19288         
19289         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19290                 
19291         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19292         
19293         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19294         
19295         this.pop = this.picker().select('>.datepicker-time',true).first();
19296         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19297         
19298         this.picker().on('mousedown', this.onMousedown, this);
19299         this.picker().on('click', this.onClick, this);
19300         
19301         this.picker().addClass('datepicker-dropdown');
19302     
19303         this.fillTime();
19304         this.update();
19305             
19306         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19307         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19308         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19309         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19310         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19311         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19312
19313     },
19314     
19315     fireKey: function(e){
19316         if (!this.picker().isVisible()){
19317             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19318                 this.show();
19319             }
19320             return;
19321         }
19322
19323         e.preventDefault();
19324         
19325         switch(e.keyCode){
19326             case 27: // escape
19327                 this.hide();
19328                 break;
19329             case 37: // left
19330             case 39: // right
19331                 this.onTogglePeriod();
19332                 break;
19333             case 38: // up
19334                 this.onIncrementMinutes();
19335                 break;
19336             case 40: // down
19337                 this.onDecrementMinutes();
19338                 break;
19339             case 13: // enter
19340             case 9: // tab
19341                 this.setTime();
19342                 break;
19343         }
19344     },
19345     
19346     onClick: function(e) {
19347         e.stopPropagation();
19348         e.preventDefault();
19349     },
19350     
19351     picker : function()
19352     {
19353         return this.el.select('.datepicker', true).first();
19354     },
19355     
19356     fillTime: function()
19357     {    
19358         var time = this.pop.select('tbody', true).first();
19359         
19360         time.dom.innerHTML = '';
19361         
19362         time.createChild({
19363             tag: 'tr',
19364             cn: [
19365                 {
19366                     tag: 'td',
19367                     cn: [
19368                         {
19369                             tag: 'a',
19370                             href: '#',
19371                             cls: 'btn',
19372                             cn: [
19373                                 {
19374                                     tag: 'span',
19375                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19376                                 }
19377                             ]
19378                         } 
19379                     ]
19380                 },
19381                 {
19382                     tag: 'td',
19383                     cls: 'separator'
19384                 },
19385                 {
19386                     tag: 'td',
19387                     cn: [
19388                         {
19389                             tag: 'a',
19390                             href: '#',
19391                             cls: 'btn',
19392                             cn: [
19393                                 {
19394                                     tag: 'span',
19395                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19396                                 }
19397                             ]
19398                         }
19399                     ]
19400                 },
19401                 {
19402                     tag: 'td',
19403                     cls: 'separator'
19404                 }
19405             ]
19406         });
19407         
19408         time.createChild({
19409             tag: 'tr',
19410             cn: [
19411                 {
19412                     tag: 'td',
19413                     cn: [
19414                         {
19415                             tag: 'span',
19416                             cls: 'timepicker-hour',
19417                             html: '00'
19418                         }  
19419                     ]
19420                 },
19421                 {
19422                     tag: 'td',
19423                     cls: 'separator',
19424                     html: ':'
19425                 },
19426                 {
19427                     tag: 'td',
19428                     cn: [
19429                         {
19430                             tag: 'span',
19431                             cls: 'timepicker-minute',
19432                             html: '00'
19433                         }  
19434                     ]
19435                 },
19436                 {
19437                     tag: 'td',
19438                     cls: 'separator'
19439                 },
19440                 {
19441                     tag: 'td',
19442                     cn: [
19443                         {
19444                             tag: 'button',
19445                             type: 'button',
19446                             cls: 'btn btn-primary period',
19447                             html: 'AM'
19448                             
19449                         }
19450                     ]
19451                 }
19452             ]
19453         });
19454         
19455         time.createChild({
19456             tag: 'tr',
19457             cn: [
19458                 {
19459                     tag: 'td',
19460                     cn: [
19461                         {
19462                             tag: 'a',
19463                             href: '#',
19464                             cls: 'btn',
19465                             cn: [
19466                                 {
19467                                     tag: 'span',
19468                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19469                                 }
19470                             ]
19471                         }
19472                     ]
19473                 },
19474                 {
19475                     tag: 'td',
19476                     cls: 'separator'
19477                 },
19478                 {
19479                     tag: 'td',
19480                     cn: [
19481                         {
19482                             tag: 'a',
19483                             href: '#',
19484                             cls: 'btn',
19485                             cn: [
19486                                 {
19487                                     tag: 'span',
19488                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19489                                 }
19490                             ]
19491                         }
19492                     ]
19493                 },
19494                 {
19495                     tag: 'td',
19496                     cls: 'separator'
19497                 }
19498             ]
19499         });
19500         
19501     },
19502     
19503     update: function()
19504     {
19505         
19506         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19507         
19508         this.fill();
19509     },
19510     
19511     fill: function() 
19512     {
19513         var hours = this.time.getHours();
19514         var minutes = this.time.getMinutes();
19515         var period = 'AM';
19516         
19517         if(hours > 11){
19518             period = 'PM';
19519         }
19520         
19521         if(hours == 0){
19522             hours = 12;
19523         }
19524         
19525         
19526         if(hours > 12){
19527             hours = hours - 12;
19528         }
19529         
19530         if(hours < 10){
19531             hours = '0' + hours;
19532         }
19533         
19534         if(minutes < 10){
19535             minutes = '0' + minutes;
19536         }
19537         
19538         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19539         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19540         this.pop.select('button', true).first().dom.innerHTML = period;
19541         
19542     },
19543     
19544     place: function()
19545     {   
19546         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19547         
19548         var cls = ['bottom'];
19549         
19550         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19551             cls.pop();
19552             cls.push('top');
19553         }
19554         
19555         cls.push('right');
19556         
19557         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19558             cls.pop();
19559             cls.push('left');
19560         }
19561         
19562         this.picker().addClass(cls.join('-'));
19563         
19564         var _this = this;
19565         
19566         Roo.each(cls, function(c){
19567             if(c == 'bottom'){
19568                 _this.picker().setTop(_this.inputEl().getHeight());
19569                 return;
19570             }
19571             if(c == 'top'){
19572                 _this.picker().setTop(0 - _this.picker().getHeight());
19573                 return;
19574             }
19575             
19576             if(c == 'left'){
19577                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19578                 return;
19579             }
19580             if(c == 'right'){
19581                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19582                 return;
19583             }
19584         });
19585         
19586     },
19587   
19588     onFocus : function()
19589     {
19590         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19591         this.show();
19592     },
19593     
19594     onBlur : function()
19595     {
19596         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19597         this.hide();
19598     },
19599     
19600     show : function()
19601     {
19602         this.picker().show();
19603         this.pop.show();
19604         this.update();
19605         this.place();
19606         
19607         this.fireEvent('show', this, this.date);
19608     },
19609     
19610     hide : function()
19611     {
19612         this.picker().hide();
19613         this.pop.hide();
19614         
19615         this.fireEvent('hide', this, this.date);
19616     },
19617     
19618     setTime : function()
19619     {
19620         this.hide();
19621         this.setValue(this.time.format(this.format));
19622         
19623         this.fireEvent('select', this, this.date);
19624         
19625         
19626     },
19627     
19628     onMousedown: function(e){
19629         e.stopPropagation();
19630         e.preventDefault();
19631     },
19632     
19633     onIncrementHours: function()
19634     {
19635         Roo.log('onIncrementHours');
19636         this.time = this.time.add(Date.HOUR, 1);
19637         this.update();
19638         
19639     },
19640     
19641     onDecrementHours: function()
19642     {
19643         Roo.log('onDecrementHours');
19644         this.time = this.time.add(Date.HOUR, -1);
19645         this.update();
19646     },
19647     
19648     onIncrementMinutes: function()
19649     {
19650         Roo.log('onIncrementMinutes');
19651         this.time = this.time.add(Date.MINUTE, 1);
19652         this.update();
19653     },
19654     
19655     onDecrementMinutes: function()
19656     {
19657         Roo.log('onDecrementMinutes');
19658         this.time = this.time.add(Date.MINUTE, -1);
19659         this.update();
19660     },
19661     
19662     onTogglePeriod: function()
19663     {
19664         Roo.log('onTogglePeriod');
19665         this.time = this.time.add(Date.HOUR, 12);
19666         this.update();
19667     }
19668     
19669    
19670 });
19671
19672 Roo.apply(Roo.bootstrap.TimeField,  {
19673     
19674     content : {
19675         tag: 'tbody',
19676         cn: [
19677             {
19678                 tag: 'tr',
19679                 cn: [
19680                 {
19681                     tag: 'td',
19682                     colspan: '7'
19683                 }
19684                 ]
19685             }
19686         ]
19687     },
19688     
19689     footer : {
19690         tag: 'tfoot',
19691         cn: [
19692             {
19693                 tag: 'tr',
19694                 cn: [
19695                 {
19696                     tag: 'th',
19697                     colspan: '7',
19698                     cls: '',
19699                     cn: [
19700                         {
19701                             tag: 'button',
19702                             cls: 'btn btn-info ok',
19703                             html: 'OK'
19704                         }
19705                     ]
19706                 }
19707
19708                 ]
19709             }
19710         ]
19711     }
19712 });
19713
19714 Roo.apply(Roo.bootstrap.TimeField,  {
19715   
19716     template : {
19717         tag: 'div',
19718         cls: 'datepicker dropdown-menu',
19719         cn: [
19720             {
19721                 tag: 'div',
19722                 cls: 'datepicker-time',
19723                 cn: [
19724                 {
19725                     tag: 'table',
19726                     cls: 'table-condensed',
19727                     cn:[
19728                     Roo.bootstrap.TimeField.content,
19729                     Roo.bootstrap.TimeField.footer
19730                     ]
19731                 }
19732                 ]
19733             }
19734         ]
19735     }
19736 });
19737
19738  
19739
19740  /*
19741  * - LGPL
19742  *
19743  * MonthField
19744  * 
19745  */
19746
19747 /**
19748  * @class Roo.bootstrap.MonthField
19749  * @extends Roo.bootstrap.Input
19750  * Bootstrap MonthField class
19751  * 
19752  * @cfg {String} language default en
19753  * 
19754  * @constructor
19755  * Create a new MonthField
19756  * @param {Object} config The config object
19757  */
19758
19759 Roo.bootstrap.MonthField = function(config){
19760     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19761     
19762     this.addEvents({
19763         /**
19764          * @event show
19765          * Fires when this field show.
19766          * @param {Roo.bootstrap.MonthField} this
19767          * @param {Mixed} date The date value
19768          */
19769         show : true,
19770         /**
19771          * @event show
19772          * Fires when this field hide.
19773          * @param {Roo.bootstrap.MonthField} this
19774          * @param {Mixed} date The date value
19775          */
19776         hide : true,
19777         /**
19778          * @event select
19779          * Fires when select a date.
19780          * @param {Roo.bootstrap.MonthField} this
19781          * @param {String} oldvalue The old value
19782          * @param {String} newvalue The new value
19783          */
19784         select : true
19785     });
19786 };
19787
19788 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19789     
19790     onRender: function(ct, position)
19791     {
19792         
19793         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19794         
19795         this.language = this.language || 'en';
19796         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19797         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19798         
19799         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19800         this.isInline = false;
19801         this.isInput = true;
19802         this.component = this.el.select('.add-on', true).first() || false;
19803         this.component = (this.component && this.component.length === 0) ? false : this.component;
19804         this.hasInput = this.component && this.inputEL().length;
19805         
19806         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19807         
19808         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19809         
19810         this.picker().on('mousedown', this.onMousedown, this);
19811         this.picker().on('click', this.onClick, this);
19812         
19813         this.picker().addClass('datepicker-dropdown');
19814         
19815         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19816             v.setStyle('width', '189px');
19817         });
19818         
19819         this.fillMonths();
19820         
19821         this.update();
19822         
19823         if(this.isInline) {
19824             this.show();
19825         }
19826         
19827     },
19828     
19829     setValue: function(v, suppressEvent)
19830     {   
19831         var o = this.getValue();
19832         
19833         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19834         
19835         this.update();
19836
19837         if(suppressEvent !== true){
19838             this.fireEvent('select', this, o, v);
19839         }
19840         
19841     },
19842     
19843     getValue: function()
19844     {
19845         return this.value;
19846     },
19847     
19848     onClick: function(e) 
19849     {
19850         e.stopPropagation();
19851         e.preventDefault();
19852         
19853         var target = e.getTarget();
19854         
19855         if(target.nodeName.toLowerCase() === 'i'){
19856             target = Roo.get(target).dom.parentNode;
19857         }
19858         
19859         var nodeName = target.nodeName;
19860         var className = target.className;
19861         var html = target.innerHTML;
19862         
19863         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19864             return;
19865         }
19866         
19867         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19868         
19869         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19870         
19871         this.hide();
19872                         
19873     },
19874     
19875     picker : function()
19876     {
19877         return this.pickerEl;
19878     },
19879     
19880     fillMonths: function()
19881     {    
19882         var i = 0;
19883         var months = this.picker().select('>.datepicker-months td', true).first();
19884         
19885         months.dom.innerHTML = '';
19886         
19887         while (i < 12) {
19888             var month = {
19889                 tag: 'span',
19890                 cls: 'month',
19891                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19892             };
19893             
19894             months.createChild(month);
19895         }
19896         
19897     },
19898     
19899     update: function()
19900     {
19901         var _this = this;
19902         
19903         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19904             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19905         }
19906         
19907         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19908             e.removeClass('active');
19909             
19910             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19911                 e.addClass('active');
19912             }
19913         })
19914     },
19915     
19916     place: function()
19917     {
19918         if(this.isInline) {
19919             return;
19920         }
19921         
19922         this.picker().removeClass(['bottom', 'top']);
19923         
19924         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19925             /*
19926              * place to the top of element!
19927              *
19928              */
19929             
19930             this.picker().addClass('top');
19931             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19932             
19933             return;
19934         }
19935         
19936         this.picker().addClass('bottom');
19937         
19938         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19939     },
19940     
19941     onFocus : function()
19942     {
19943         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19944         this.show();
19945     },
19946     
19947     onBlur : function()
19948     {
19949         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19950         
19951         var d = this.inputEl().getValue();
19952         
19953         this.setValue(d);
19954                 
19955         this.hide();
19956     },
19957     
19958     show : function()
19959     {
19960         this.picker().show();
19961         this.picker().select('>.datepicker-months', true).first().show();
19962         this.update();
19963         this.place();
19964         
19965         this.fireEvent('show', this, this.date);
19966     },
19967     
19968     hide : function()
19969     {
19970         if(this.isInline) {
19971             return;
19972         }
19973         this.picker().hide();
19974         this.fireEvent('hide', this, this.date);
19975         
19976     },
19977     
19978     onMousedown: function(e)
19979     {
19980         e.stopPropagation();
19981         e.preventDefault();
19982     },
19983     
19984     keyup: function(e)
19985     {
19986         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19987         this.update();
19988     },
19989
19990     fireKey: function(e)
19991     {
19992         if (!this.picker().isVisible()){
19993             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19994                 this.show();
19995             }
19996             return;
19997         }
19998         
19999         var dir;
20000         
20001         switch(e.keyCode){
20002             case 27: // escape
20003                 this.hide();
20004                 e.preventDefault();
20005                 break;
20006             case 37: // left
20007             case 39: // right
20008                 dir = e.keyCode == 37 ? -1 : 1;
20009                 
20010                 this.vIndex = this.vIndex + dir;
20011                 
20012                 if(this.vIndex < 0){
20013                     this.vIndex = 0;
20014                 }
20015                 
20016                 if(this.vIndex > 11){
20017                     this.vIndex = 11;
20018                 }
20019                 
20020                 if(isNaN(this.vIndex)){
20021                     this.vIndex = 0;
20022                 }
20023                 
20024                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20025                 
20026                 break;
20027             case 38: // up
20028             case 40: // down
20029                 
20030                 dir = e.keyCode == 38 ? -1 : 1;
20031                 
20032                 this.vIndex = this.vIndex + dir * 4;
20033                 
20034                 if(this.vIndex < 0){
20035                     this.vIndex = 0;
20036                 }
20037                 
20038                 if(this.vIndex > 11){
20039                     this.vIndex = 11;
20040                 }
20041                 
20042                 if(isNaN(this.vIndex)){
20043                     this.vIndex = 0;
20044                 }
20045                 
20046                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20047                 break;
20048                 
20049             case 13: // enter
20050                 
20051                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20052                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20053                 }
20054                 
20055                 this.hide();
20056                 e.preventDefault();
20057                 break;
20058             case 9: // tab
20059                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20060                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20061                 }
20062                 this.hide();
20063                 break;
20064             case 16: // shift
20065             case 17: // ctrl
20066             case 18: // alt
20067                 break;
20068             default :
20069                 this.hide();
20070                 
20071         }
20072     },
20073     
20074     remove: function() 
20075     {
20076         this.picker().remove();
20077     }
20078    
20079 });
20080
20081 Roo.apply(Roo.bootstrap.MonthField,  {
20082     
20083     content : {
20084         tag: 'tbody',
20085         cn: [
20086         {
20087             tag: 'tr',
20088             cn: [
20089             {
20090                 tag: 'td',
20091                 colspan: '7'
20092             }
20093             ]
20094         }
20095         ]
20096     },
20097     
20098     dates:{
20099         en: {
20100             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20101             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20102         }
20103     }
20104 });
20105
20106 Roo.apply(Roo.bootstrap.MonthField,  {
20107   
20108     template : {
20109         tag: 'div',
20110         cls: 'datepicker dropdown-menu roo-dynamic',
20111         cn: [
20112             {
20113                 tag: 'div',
20114                 cls: 'datepicker-months',
20115                 cn: [
20116                 {
20117                     tag: 'table',
20118                     cls: 'table-condensed',
20119                     cn:[
20120                         Roo.bootstrap.DateField.content
20121                     ]
20122                 }
20123                 ]
20124             }
20125         ]
20126     }
20127 });
20128
20129  
20130
20131  
20132  /*
20133  * - LGPL
20134  *
20135  * CheckBox
20136  * 
20137  */
20138
20139 /**
20140  * @class Roo.bootstrap.CheckBox
20141  * @extends Roo.bootstrap.Input
20142  * Bootstrap CheckBox class
20143  * 
20144  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20145  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20146  * @cfg {String} boxLabel The text that appears beside the checkbox
20147  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20148  * @cfg {Boolean} checked initnal the element
20149  * @cfg {Boolean} inline inline the element (default false)
20150  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20151  * @cfg {String} tooltip label tooltip
20152  * 
20153  * @constructor
20154  * Create a new CheckBox
20155  * @param {Object} config The config object
20156  */
20157
20158 Roo.bootstrap.CheckBox = function(config){
20159     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20160    
20161     this.addEvents({
20162         /**
20163         * @event check
20164         * Fires when the element is checked or unchecked.
20165         * @param {Roo.bootstrap.CheckBox} this This input
20166         * @param {Boolean} checked The new checked value
20167         */
20168        check : true,
20169        /**
20170         * @event click
20171         * Fires when the element is click.
20172         * @param {Roo.bootstrap.CheckBox} this This input
20173         */
20174        click : true
20175     });
20176     
20177 };
20178
20179 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20180   
20181     inputType: 'checkbox',
20182     inputValue: 1,
20183     valueOff: 0,
20184     boxLabel: false,
20185     checked: false,
20186     weight : false,
20187     inline: false,
20188     tooltip : '',
20189     
20190     getAutoCreate : function()
20191     {
20192         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20193         
20194         var id = Roo.id();
20195         
20196         var cfg = {};
20197         
20198         cfg.cls = 'form-group ' + this.inputType; //input-group
20199         
20200         if(this.inline){
20201             cfg.cls += ' ' + this.inputType + '-inline';
20202         }
20203         
20204         var input =  {
20205             tag: 'input',
20206             id : id,
20207             type : this.inputType,
20208             value : this.inputValue,
20209             cls : 'roo-' + this.inputType, //'form-box',
20210             placeholder : this.placeholder || ''
20211             
20212         };
20213         
20214         if(this.inputType != 'radio'){
20215             var hidden =  {
20216                 tag: 'input',
20217                 type : 'hidden',
20218                 cls : 'roo-hidden-value',
20219                 value : this.checked ? this.inputValue : this.valueOff
20220             };
20221         }
20222         
20223             
20224         if (this.weight) { // Validity check?
20225             cfg.cls += " " + this.inputType + "-" + this.weight;
20226         }
20227         
20228         if (this.disabled) {
20229             input.disabled=true;
20230         }
20231         
20232         if(this.checked){
20233             input.checked = this.checked;
20234         }
20235         
20236         if (this.name) {
20237             
20238             input.name = this.name;
20239             
20240             if(this.inputType != 'radio'){
20241                 hidden.name = this.name;
20242                 input.name = '_hidden_' + this.name;
20243             }
20244         }
20245         
20246         if (this.size) {
20247             input.cls += ' input-' + this.size;
20248         }
20249         
20250         var settings=this;
20251         
20252         ['xs','sm','md','lg'].map(function(size){
20253             if (settings[size]) {
20254                 cfg.cls += ' col-' + size + '-' + settings[size];
20255             }
20256         });
20257         
20258         var inputblock = input;
20259          
20260         if (this.before || this.after) {
20261             
20262             inputblock = {
20263                 cls : 'input-group',
20264                 cn :  [] 
20265             };
20266             
20267             if (this.before) {
20268                 inputblock.cn.push({
20269                     tag :'span',
20270                     cls : 'input-group-addon',
20271                     html : this.before
20272                 });
20273             }
20274             
20275             inputblock.cn.push(input);
20276             
20277             if(this.inputType != 'radio'){
20278                 inputblock.cn.push(hidden);
20279             }
20280             
20281             if (this.after) {
20282                 inputblock.cn.push({
20283                     tag :'span',
20284                     cls : 'input-group-addon',
20285                     html : this.after
20286                 });
20287             }
20288             
20289         }
20290         
20291         if (align ==='left' && this.fieldLabel.length) {
20292 //                Roo.log("left and has label");
20293             cfg.cn = [
20294                 {
20295                     tag: 'label',
20296                     'for' :  id,
20297                     cls : 'control-label',
20298                     html : this.fieldLabel
20299                 },
20300                 {
20301                     cls : "", 
20302                     cn: [
20303                         inputblock
20304                     ]
20305                 }
20306             ];
20307             
20308             if(this.labelWidth > 12){
20309                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20310             }
20311             
20312             if(this.labelWidth < 13 && this.labelmd == 0){
20313                 this.labelmd = this.labelWidth;
20314             }
20315             
20316             if(this.labellg > 0){
20317                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20318                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20319             }
20320             
20321             if(this.labelmd > 0){
20322                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20323                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20324             }
20325             
20326             if(this.labelsm > 0){
20327                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20328                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20329             }
20330             
20331             if(this.labelxs > 0){
20332                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20333                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20334             }
20335             
20336         } else if ( this.fieldLabel.length) {
20337 //                Roo.log(" label");
20338                 cfg.cn = [
20339                    
20340                     {
20341                         tag: this.boxLabel ? 'span' : 'label',
20342                         'for': id,
20343                         cls: 'control-label box-input-label',
20344                         //cls : 'input-group-addon',
20345                         html : this.fieldLabel
20346                     },
20347                     
20348                     inputblock
20349                     
20350                 ];
20351
20352         } else {
20353             
20354 //                Roo.log(" no label && no align");
20355                 cfg.cn = [  inputblock ] ;
20356                 
20357                 
20358         }
20359         
20360         if(this.boxLabel){
20361              var boxLabelCfg = {
20362                 tag: 'label',
20363                 //'for': id, // box label is handled by onclick - so no for...
20364                 cls: 'box-label',
20365                 html: this.boxLabel
20366             };
20367             
20368             if(this.tooltip){
20369                 boxLabelCfg.tooltip = this.tooltip;
20370             }
20371              
20372             cfg.cn.push(boxLabelCfg);
20373         }
20374         
20375         if(this.inputType != 'radio'){
20376             cfg.cn.push(hidden);
20377         }
20378         
20379         return cfg;
20380         
20381     },
20382     
20383     /**
20384      * return the real input element.
20385      */
20386     inputEl: function ()
20387     {
20388         return this.el.select('input.roo-' + this.inputType,true).first();
20389     },
20390     hiddenEl: function ()
20391     {
20392         return this.el.select('input.roo-hidden-value',true).first();
20393     },
20394     
20395     labelEl: function()
20396     {
20397         return this.el.select('label.control-label',true).first();
20398     },
20399     /* depricated... */
20400     
20401     label: function()
20402     {
20403         return this.labelEl();
20404     },
20405     
20406     boxLabelEl: function()
20407     {
20408         return this.el.select('label.box-label',true).first();
20409     },
20410     
20411     initEvents : function()
20412     {
20413 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20414         
20415         this.inputEl().on('click', this.onClick,  this);
20416         
20417         if (this.boxLabel) { 
20418             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20419         }
20420         
20421         this.startValue = this.getValue();
20422         
20423         if(this.groupId){
20424             Roo.bootstrap.CheckBox.register(this);
20425         }
20426     },
20427     
20428     onClick : function(e)
20429     {   
20430         if(this.fireEvent('click', this, e) !== false){
20431             this.setChecked(!this.checked);
20432         }
20433         
20434     },
20435     
20436     setChecked : function(state,suppressEvent)
20437     {
20438         this.startValue = this.getValue();
20439
20440         if(this.inputType == 'radio'){
20441             
20442             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20443                 e.dom.checked = false;
20444             });
20445             
20446             this.inputEl().dom.checked = true;
20447             
20448             this.inputEl().dom.value = this.inputValue;
20449             
20450             if(suppressEvent !== true){
20451                 this.fireEvent('check', this, true);
20452             }
20453             
20454             this.validate();
20455             
20456             return;
20457         }
20458         
20459         this.checked = state;
20460         
20461         this.inputEl().dom.checked = state;
20462         
20463         
20464         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20465         
20466         if(suppressEvent !== true){
20467             this.fireEvent('check', this, state);
20468         }
20469         
20470         this.validate();
20471     },
20472     
20473     getValue : function()
20474     {
20475         if(this.inputType == 'radio'){
20476             return this.getGroupValue();
20477         }
20478         
20479         return this.hiddenEl().dom.value;
20480         
20481     },
20482     
20483     getGroupValue : function()
20484     {
20485         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20486             return '';
20487         }
20488         
20489         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20490     },
20491     
20492     setValue : function(v,suppressEvent)
20493     {
20494         if(this.inputType == 'radio'){
20495             this.setGroupValue(v, suppressEvent);
20496             return;
20497         }
20498         
20499         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20500         
20501         this.validate();
20502     },
20503     
20504     setGroupValue : function(v, suppressEvent)
20505     {
20506         this.startValue = this.getValue();
20507         
20508         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20509             e.dom.checked = false;
20510             
20511             if(e.dom.value == v){
20512                 e.dom.checked = true;
20513             }
20514         });
20515         
20516         if(suppressEvent !== true){
20517             this.fireEvent('check', this, true);
20518         }
20519
20520         this.validate();
20521         
20522         return;
20523     },
20524     
20525     validate : function()
20526     {
20527         if(
20528                 this.disabled || 
20529                 (this.inputType == 'radio' && this.validateRadio()) ||
20530                 (this.inputType == 'checkbox' && this.validateCheckbox())
20531         ){
20532             this.markValid();
20533             return true;
20534         }
20535         
20536         this.markInvalid();
20537         return false;
20538     },
20539     
20540     validateRadio : function()
20541     {
20542         if(this.allowBlank){
20543             return true;
20544         }
20545         
20546         var valid = false;
20547         
20548         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20549             if(!e.dom.checked){
20550                 return;
20551             }
20552             
20553             valid = true;
20554             
20555             return false;
20556         });
20557         
20558         return valid;
20559     },
20560     
20561     validateCheckbox : function()
20562     {
20563         if(!this.groupId){
20564             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20565             //return (this.getValue() == this.inputValue) ? true : false;
20566         }
20567         
20568         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20569         
20570         if(!group){
20571             return false;
20572         }
20573         
20574         var r = false;
20575         
20576         for(var i in group){
20577             if(group[i].el.isVisible(true)){
20578                 r = false;
20579                 break;
20580             }
20581             
20582             r = true;
20583         }
20584         
20585         for(var i in group){
20586             if(r){
20587                 break;
20588             }
20589             
20590             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20591         }
20592         
20593         return r;
20594     },
20595     
20596     /**
20597      * Mark this field as valid
20598      */
20599     markValid : function()
20600     {
20601         var _this = this;
20602         
20603         this.fireEvent('valid', this);
20604         
20605         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20606         
20607         if(this.groupId){
20608             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20609         }
20610         
20611         if(label){
20612             label.markValid();
20613         }
20614
20615         if(this.inputType == 'radio'){
20616             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20617                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20618                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20619             });
20620             
20621             return;
20622         }
20623
20624         if(!this.groupId){
20625             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20626             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20627             return;
20628         }
20629         
20630         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20631         
20632         if(!group){
20633             return;
20634         }
20635         
20636         for(var i in group){
20637             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20638             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20639         }
20640     },
20641     
20642      /**
20643      * Mark this field as invalid
20644      * @param {String} msg The validation message
20645      */
20646     markInvalid : function(msg)
20647     {
20648         if(this.allowBlank){
20649             return;
20650         }
20651         
20652         var _this = this;
20653         
20654         this.fireEvent('invalid', this, msg);
20655         
20656         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20657         
20658         if(this.groupId){
20659             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20660         }
20661         
20662         if(label){
20663             label.markInvalid();
20664         }
20665             
20666         if(this.inputType == 'radio'){
20667             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20668                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20669                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20670             });
20671             
20672             return;
20673         }
20674         
20675         if(!this.groupId){
20676             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20677             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20678             return;
20679         }
20680         
20681         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20682         
20683         if(!group){
20684             return;
20685         }
20686         
20687         for(var i in group){
20688             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20689             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20690         }
20691         
20692     },
20693     
20694     clearInvalid : function()
20695     {
20696         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20697         
20698         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20699         
20700         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20701         
20702         if (label && label.iconEl) {
20703             label.iconEl.removeClass(label.validClass);
20704             label.iconEl.removeClass(label.invalidClass);
20705         }
20706     },
20707     
20708     disable : function()
20709     {
20710         if(this.inputType != 'radio'){
20711             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20712             return;
20713         }
20714         
20715         var _this = this;
20716         
20717         if(this.rendered){
20718             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20719                 _this.getActionEl().addClass(this.disabledClass);
20720                 e.dom.disabled = true;
20721             });
20722         }
20723         
20724         this.disabled = true;
20725         this.fireEvent("disable", this);
20726         return this;
20727     },
20728
20729     enable : function()
20730     {
20731         if(this.inputType != 'radio'){
20732             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20733             return;
20734         }
20735         
20736         var _this = this;
20737         
20738         if(this.rendered){
20739             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20740                 _this.getActionEl().removeClass(this.disabledClass);
20741                 e.dom.disabled = false;
20742             });
20743         }
20744         
20745         this.disabled = false;
20746         this.fireEvent("enable", this);
20747         return this;
20748     }
20749
20750 });
20751
20752 Roo.apply(Roo.bootstrap.CheckBox, {
20753     
20754     groups: {},
20755     
20756      /**
20757     * register a CheckBox Group
20758     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20759     */
20760     register : function(checkbox)
20761     {
20762         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20763             this.groups[checkbox.groupId] = {};
20764         }
20765         
20766         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20767             return;
20768         }
20769         
20770         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20771         
20772     },
20773     /**
20774     * fetch a CheckBox Group based on the group ID
20775     * @param {string} the group ID
20776     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20777     */
20778     get: function(groupId) {
20779         if (typeof(this.groups[groupId]) == 'undefined') {
20780             return false;
20781         }
20782         
20783         return this.groups[groupId] ;
20784     }
20785     
20786     
20787 });
20788 /*
20789  * - LGPL
20790  *
20791  * RadioItem
20792  * 
20793  */
20794
20795 /**
20796  * @class Roo.bootstrap.Radio
20797  * @extends Roo.bootstrap.Component
20798  * Bootstrap Radio class
20799  * @cfg {String} boxLabel - the label associated
20800  * @cfg {String} value - the value of radio
20801  * 
20802  * @constructor
20803  * Create a new Radio
20804  * @param {Object} config The config object
20805  */
20806 Roo.bootstrap.Radio = function(config){
20807     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20808     
20809 };
20810
20811 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20812     
20813     boxLabel : '',
20814     
20815     value : '',
20816     
20817     getAutoCreate : function()
20818     {
20819         var cfg = {
20820             tag : 'div',
20821             cls : 'form-group radio',
20822             cn : [
20823                 {
20824                     tag : 'label',
20825                     cls : 'box-label',
20826                     html : this.boxLabel
20827                 }
20828             ]
20829         };
20830         
20831         return cfg;
20832     },
20833     
20834     initEvents : function() 
20835     {
20836         this.parent().register(this);
20837         
20838         this.el.on('click', this.onClick, this);
20839         
20840     },
20841     
20842     onClick : function()
20843     {
20844         this.setChecked(true);
20845     },
20846     
20847     setChecked : function(state, suppressEvent)
20848     {
20849         this.parent().setValue(this.value, suppressEvent);
20850         
20851     },
20852     
20853     setBoxLabel : function(v)
20854     {
20855         this.boxLabel = v;
20856         
20857         if(this.rendered){
20858             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20859         }
20860     }
20861     
20862 });
20863  
20864
20865  /*
20866  * - LGPL
20867  *
20868  * Input
20869  * 
20870  */
20871
20872 /**
20873  * @class Roo.bootstrap.SecurePass
20874  * @extends Roo.bootstrap.Input
20875  * Bootstrap SecurePass class
20876  *
20877  * 
20878  * @constructor
20879  * Create a new SecurePass
20880  * @param {Object} config The config object
20881  */
20882  
20883 Roo.bootstrap.SecurePass = function (config) {
20884     // these go here, so the translation tool can replace them..
20885     this.errors = {
20886         PwdEmpty: "Please type a password, and then retype it to confirm.",
20887         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20888         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20889         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20890         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20891         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20892         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20893         TooWeak: "Your password is Too Weak."
20894     },
20895     this.meterLabel = "Password strength:";
20896     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20897     this.meterClass = [
20898         "roo-password-meter-tooweak", 
20899         "roo-password-meter-weak", 
20900         "roo-password-meter-medium", 
20901         "roo-password-meter-strong", 
20902         "roo-password-meter-grey"
20903     ];
20904     
20905     this.errors = {};
20906     
20907     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20908 }
20909
20910 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20911     /**
20912      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20913      * {
20914      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20915      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20916      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20917      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20918      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20919      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20920      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20921      * })
20922      */
20923     // private
20924     
20925     meterWidth: 300,
20926     errorMsg :'',    
20927     errors: false,
20928     imageRoot: '/',
20929     /**
20930      * @cfg {String/Object} Label for the strength meter (defaults to
20931      * 'Password strength:')
20932      */
20933     // private
20934     meterLabel: '',
20935     /**
20936      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20937      * ['Weak', 'Medium', 'Strong'])
20938      */
20939     // private    
20940     pwdStrengths: false,    
20941     // private
20942     strength: 0,
20943     // private
20944     _lastPwd: null,
20945     // private
20946     kCapitalLetter: 0,
20947     kSmallLetter: 1,
20948     kDigit: 2,
20949     kPunctuation: 3,
20950     
20951     insecure: false,
20952     // private
20953     initEvents: function ()
20954     {
20955         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20956
20957         if (this.el.is('input[type=password]') && Roo.isSafari) {
20958             this.el.on('keydown', this.SafariOnKeyDown, this);
20959         }
20960
20961         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20962     },
20963     // private
20964     onRender: function (ct, position)
20965     {
20966         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20967         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20968         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20969
20970         this.trigger.createChild({
20971                    cn: [
20972                     {
20973                     //id: 'PwdMeter',
20974                     tag: 'div',
20975                     cls: 'roo-password-meter-grey col-xs-12',
20976                     style: {
20977                         //width: 0,
20978                         //width: this.meterWidth + 'px'                                                
20979                         }
20980                     },
20981                     {                            
20982                          cls: 'roo-password-meter-text'                          
20983                     }
20984                 ]            
20985         });
20986
20987          
20988         if (this.hideTrigger) {
20989             this.trigger.setDisplayed(false);
20990         }
20991         this.setSize(this.width || '', this.height || '');
20992     },
20993     // private
20994     onDestroy: function ()
20995     {
20996         if (this.trigger) {
20997             this.trigger.removeAllListeners();
20998             this.trigger.remove();
20999         }
21000         if (this.wrap) {
21001             this.wrap.remove();
21002         }
21003         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21004     },
21005     // private
21006     checkStrength: function ()
21007     {
21008         var pwd = this.inputEl().getValue();
21009         if (pwd == this._lastPwd) {
21010             return;
21011         }
21012
21013         var strength;
21014         if (this.ClientSideStrongPassword(pwd)) {
21015             strength = 3;
21016         } else if (this.ClientSideMediumPassword(pwd)) {
21017             strength = 2;
21018         } else if (this.ClientSideWeakPassword(pwd)) {
21019             strength = 1;
21020         } else {
21021             strength = 0;
21022         }
21023         
21024         Roo.log('strength1: ' + strength);
21025         
21026         //var pm = this.trigger.child('div/div/div').dom;
21027         var pm = this.trigger.child('div/div');
21028         pm.removeClass(this.meterClass);
21029         pm.addClass(this.meterClass[strength]);
21030                 
21031         
21032         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21033                 
21034         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21035         
21036         this._lastPwd = pwd;
21037     },
21038     reset: function ()
21039     {
21040         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21041         
21042         this._lastPwd = '';
21043         
21044         var pm = this.trigger.child('div/div');
21045         pm.removeClass(this.meterClass);
21046         pm.addClass('roo-password-meter-grey');        
21047         
21048         
21049         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21050         
21051         pt.innerHTML = '';
21052         this.inputEl().dom.type='password';
21053     },
21054     // private
21055     validateValue: function (value)
21056     {
21057         
21058         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21059             return false;
21060         }
21061         if (value.length == 0) {
21062             if (this.allowBlank) {
21063                 this.clearInvalid();
21064                 return true;
21065             }
21066
21067             this.markInvalid(this.errors.PwdEmpty);
21068             this.errorMsg = this.errors.PwdEmpty;
21069             return false;
21070         }
21071         
21072         if(this.insecure){
21073             return true;
21074         }
21075         
21076         if ('[\x21-\x7e]*'.match(value)) {
21077             this.markInvalid(this.errors.PwdBadChar);
21078             this.errorMsg = this.errors.PwdBadChar;
21079             return false;
21080         }
21081         if (value.length < 6) {
21082             this.markInvalid(this.errors.PwdShort);
21083             this.errorMsg = this.errors.PwdShort;
21084             return false;
21085         }
21086         if (value.length > 16) {
21087             this.markInvalid(this.errors.PwdLong);
21088             this.errorMsg = this.errors.PwdLong;
21089             return false;
21090         }
21091         var strength;
21092         if (this.ClientSideStrongPassword(value)) {
21093             strength = 3;
21094         } else if (this.ClientSideMediumPassword(value)) {
21095             strength = 2;
21096         } else if (this.ClientSideWeakPassword(value)) {
21097             strength = 1;
21098         } else {
21099             strength = 0;
21100         }
21101
21102         
21103         if (strength < 2) {
21104             //this.markInvalid(this.errors.TooWeak);
21105             this.errorMsg = this.errors.TooWeak;
21106             //return false;
21107         }
21108         
21109         
21110         console.log('strength2: ' + strength);
21111         
21112         //var pm = this.trigger.child('div/div/div').dom;
21113         
21114         var pm = this.trigger.child('div/div');
21115         pm.removeClass(this.meterClass);
21116         pm.addClass(this.meterClass[strength]);
21117                 
21118         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21119                 
21120         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21121         
21122         this.errorMsg = ''; 
21123         return true;
21124     },
21125     // private
21126     CharacterSetChecks: function (type)
21127     {
21128         this.type = type;
21129         this.fResult = false;
21130     },
21131     // private
21132     isctype: function (character, type)
21133     {
21134         switch (type) {  
21135             case this.kCapitalLetter:
21136                 if (character >= 'A' && character <= 'Z') {
21137                     return true;
21138                 }
21139                 break;
21140             
21141             case this.kSmallLetter:
21142                 if (character >= 'a' && character <= 'z') {
21143                     return true;
21144                 }
21145                 break;
21146             
21147             case this.kDigit:
21148                 if (character >= '0' && character <= '9') {
21149                     return true;
21150                 }
21151                 break;
21152             
21153             case this.kPunctuation:
21154                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21155                     return true;
21156                 }
21157                 break;
21158             
21159             default:
21160                 return false;
21161         }
21162
21163     },
21164     // private
21165     IsLongEnough: function (pwd, size)
21166     {
21167         return !(pwd == null || isNaN(size) || pwd.length < size);
21168     },
21169     // private
21170     SpansEnoughCharacterSets: function (word, nb)
21171     {
21172         if (!this.IsLongEnough(word, nb))
21173         {
21174             return false;
21175         }
21176
21177         var characterSetChecks = new Array(
21178             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21179             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21180         );
21181         
21182         for (var index = 0; index < word.length; ++index) {
21183             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21184                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21185                     characterSetChecks[nCharSet].fResult = true;
21186                     break;
21187                 }
21188             }
21189         }
21190
21191         var nCharSets = 0;
21192         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21193             if (characterSetChecks[nCharSet].fResult) {
21194                 ++nCharSets;
21195             }
21196         }
21197
21198         if (nCharSets < nb) {
21199             return false;
21200         }
21201         return true;
21202     },
21203     // private
21204     ClientSideStrongPassword: function (pwd)
21205     {
21206         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21207     },
21208     // private
21209     ClientSideMediumPassword: function (pwd)
21210     {
21211         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21212     },
21213     // private
21214     ClientSideWeakPassword: function (pwd)
21215     {
21216         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21217     }
21218           
21219 })//<script type="text/javascript">
21220
21221 /*
21222  * Based  Ext JS Library 1.1.1
21223  * Copyright(c) 2006-2007, Ext JS, LLC.
21224  * LGPL
21225  *
21226  */
21227  
21228 /**
21229  * @class Roo.HtmlEditorCore
21230  * @extends Roo.Component
21231  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21232  *
21233  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21234  */
21235
21236 Roo.HtmlEditorCore = function(config){
21237     
21238     
21239     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21240     
21241     
21242     this.addEvents({
21243         /**
21244          * @event initialize
21245          * Fires when the editor is fully initialized (including the iframe)
21246          * @param {Roo.HtmlEditorCore} this
21247          */
21248         initialize: true,
21249         /**
21250          * @event activate
21251          * Fires when the editor is first receives the focus. Any insertion must wait
21252          * until after this event.
21253          * @param {Roo.HtmlEditorCore} this
21254          */
21255         activate: true,
21256          /**
21257          * @event beforesync
21258          * Fires before the textarea is updated with content from the editor iframe. Return false
21259          * to cancel the sync.
21260          * @param {Roo.HtmlEditorCore} this
21261          * @param {String} html
21262          */
21263         beforesync: true,
21264          /**
21265          * @event beforepush
21266          * Fires before the iframe editor is updated with content from the textarea. Return false
21267          * to cancel the push.
21268          * @param {Roo.HtmlEditorCore} this
21269          * @param {String} html
21270          */
21271         beforepush: true,
21272          /**
21273          * @event sync
21274          * Fires when the textarea is updated with content from the editor iframe.
21275          * @param {Roo.HtmlEditorCore} this
21276          * @param {String} html
21277          */
21278         sync: true,
21279          /**
21280          * @event push
21281          * Fires when the iframe editor is updated with content from the textarea.
21282          * @param {Roo.HtmlEditorCore} this
21283          * @param {String} html
21284          */
21285         push: true,
21286         
21287         /**
21288          * @event editorevent
21289          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21290          * @param {Roo.HtmlEditorCore} this
21291          */
21292         editorevent: true
21293         
21294     });
21295     
21296     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21297     
21298     // defaults : white / black...
21299     this.applyBlacklists();
21300     
21301     
21302     
21303 };
21304
21305
21306 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21307
21308
21309      /**
21310      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21311      */
21312     
21313     owner : false,
21314     
21315      /**
21316      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21317      *                        Roo.resizable.
21318      */
21319     resizable : false,
21320      /**
21321      * @cfg {Number} height (in pixels)
21322      */   
21323     height: 300,
21324    /**
21325      * @cfg {Number} width (in pixels)
21326      */   
21327     width: 500,
21328     
21329     /**
21330      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21331      * 
21332      */
21333     stylesheets: false,
21334     
21335     // id of frame..
21336     frameId: false,
21337     
21338     // private properties
21339     validationEvent : false,
21340     deferHeight: true,
21341     initialized : false,
21342     activated : false,
21343     sourceEditMode : false,
21344     onFocus : Roo.emptyFn,
21345     iframePad:3,
21346     hideMode:'offsets',
21347     
21348     clearUp: true,
21349     
21350     // blacklist + whitelisted elements..
21351     black: false,
21352     white: false,
21353      
21354     bodyCls : '',
21355
21356     /**
21357      * Protected method that will not generally be called directly. It
21358      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21359      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21360      */
21361     getDocMarkup : function(){
21362         // body styles..
21363         var st = '';
21364         
21365         // inherit styels from page...?? 
21366         if (this.stylesheets === false) {
21367             
21368             Roo.get(document.head).select('style').each(function(node) {
21369                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21370             });
21371             
21372             Roo.get(document.head).select('link').each(function(node) { 
21373                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21374             });
21375             
21376         } else if (!this.stylesheets.length) {
21377                 // simple..
21378                 st = '<style type="text/css">' +
21379                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21380                    '</style>';
21381         } else { 
21382             st = '<style type="text/css">' +
21383                     this.stylesheets +
21384                 '</style>';
21385         }
21386         
21387         st +=  '<style type="text/css">' +
21388             'IMG { cursor: pointer } ' +
21389         '</style>';
21390
21391         var cls = 'roo-htmleditor-body';
21392         
21393         if(this.bodyCls.length){
21394             cls += ' ' + this.bodyCls;
21395         }
21396         
21397         return '<html><head>' + st  +
21398             //<style type="text/css">' +
21399             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21400             //'</style>' +
21401             ' </head><body class="' +  cls + '"></body></html>';
21402     },
21403
21404     // private
21405     onRender : function(ct, position)
21406     {
21407         var _t = this;
21408         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21409         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21410         
21411         
21412         this.el.dom.style.border = '0 none';
21413         this.el.dom.setAttribute('tabIndex', -1);
21414         this.el.addClass('x-hidden hide');
21415         
21416         
21417         
21418         if(Roo.isIE){ // fix IE 1px bogus margin
21419             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21420         }
21421        
21422         
21423         this.frameId = Roo.id();
21424         
21425          
21426         
21427         var iframe = this.owner.wrap.createChild({
21428             tag: 'iframe',
21429             cls: 'form-control', // bootstrap..
21430             id: this.frameId,
21431             name: this.frameId,
21432             frameBorder : 'no',
21433             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21434         }, this.el
21435         );
21436         
21437         
21438         this.iframe = iframe.dom;
21439
21440          this.assignDocWin();
21441         
21442         this.doc.designMode = 'on';
21443        
21444         this.doc.open();
21445         this.doc.write(this.getDocMarkup());
21446         this.doc.close();
21447
21448         
21449         var task = { // must defer to wait for browser to be ready
21450             run : function(){
21451                 //console.log("run task?" + this.doc.readyState);
21452                 this.assignDocWin();
21453                 if(this.doc.body || this.doc.readyState == 'complete'){
21454                     try {
21455                         this.doc.designMode="on";
21456                     } catch (e) {
21457                         return;
21458                     }
21459                     Roo.TaskMgr.stop(task);
21460                     this.initEditor.defer(10, this);
21461                 }
21462             },
21463             interval : 10,
21464             duration: 10000,
21465             scope: this
21466         };
21467         Roo.TaskMgr.start(task);
21468
21469     },
21470
21471     // private
21472     onResize : function(w, h)
21473     {
21474          Roo.log('resize: ' +w + ',' + h );
21475         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21476         if(!this.iframe){
21477             return;
21478         }
21479         if(typeof w == 'number'){
21480             
21481             this.iframe.style.width = w + 'px';
21482         }
21483         if(typeof h == 'number'){
21484             
21485             this.iframe.style.height = h + 'px';
21486             if(this.doc){
21487                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21488             }
21489         }
21490         
21491     },
21492
21493     /**
21494      * Toggles the editor between standard and source edit mode.
21495      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21496      */
21497     toggleSourceEdit : function(sourceEditMode){
21498         
21499         this.sourceEditMode = sourceEditMode === true;
21500         
21501         if(this.sourceEditMode){
21502  
21503             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21504             
21505         }else{
21506             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21507             //this.iframe.className = '';
21508             this.deferFocus();
21509         }
21510         //this.setSize(this.owner.wrap.getSize());
21511         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21512     },
21513
21514     
21515   
21516
21517     /**
21518      * Protected method that will not generally be called directly. If you need/want
21519      * custom HTML cleanup, this is the method you should override.
21520      * @param {String} html The HTML to be cleaned
21521      * return {String} The cleaned HTML
21522      */
21523     cleanHtml : function(html){
21524         html = String(html);
21525         if(html.length > 5){
21526             if(Roo.isSafari){ // strip safari nonsense
21527                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21528             }
21529         }
21530         if(html == '&nbsp;'){
21531             html = '';
21532         }
21533         return html;
21534     },
21535
21536     /**
21537      * HTML Editor -> Textarea
21538      * Protected method that will not generally be called directly. Syncs the contents
21539      * of the editor iframe with the textarea.
21540      */
21541     syncValue : function(){
21542         if(this.initialized){
21543             var bd = (this.doc.body || this.doc.documentElement);
21544             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21545             var html = bd.innerHTML;
21546             if(Roo.isSafari){
21547                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21548                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21549                 if(m && m[1]){
21550                     html = '<div style="'+m[0]+'">' + html + '</div>';
21551                 }
21552             }
21553             html = this.cleanHtml(html);
21554             // fix up the special chars.. normaly like back quotes in word...
21555             // however we do not want to do this with chinese..
21556             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21557                 var cc = b.charCodeAt();
21558                 if (
21559                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21560                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21561                     (cc >= 0xf900 && cc < 0xfb00 )
21562                 ) {
21563                         return b;
21564                 }
21565                 return "&#"+cc+";" 
21566             });
21567             if(this.owner.fireEvent('beforesync', this, html) !== false){
21568                 this.el.dom.value = html;
21569                 this.owner.fireEvent('sync', this, html);
21570             }
21571         }
21572     },
21573
21574     /**
21575      * Protected method that will not generally be called directly. Pushes the value of the textarea
21576      * into the iframe editor.
21577      */
21578     pushValue : function(){
21579         if(this.initialized){
21580             var v = this.el.dom.value.trim();
21581             
21582 //            if(v.length < 1){
21583 //                v = '&#160;';
21584 //            }
21585             
21586             if(this.owner.fireEvent('beforepush', this, v) !== false){
21587                 var d = (this.doc.body || this.doc.documentElement);
21588                 d.innerHTML = v;
21589                 this.cleanUpPaste();
21590                 this.el.dom.value = d.innerHTML;
21591                 this.owner.fireEvent('push', this, v);
21592             }
21593         }
21594     },
21595
21596     // private
21597     deferFocus : function(){
21598         this.focus.defer(10, this);
21599     },
21600
21601     // doc'ed in Field
21602     focus : function(){
21603         if(this.win && !this.sourceEditMode){
21604             this.win.focus();
21605         }else{
21606             this.el.focus();
21607         }
21608     },
21609     
21610     assignDocWin: function()
21611     {
21612         var iframe = this.iframe;
21613         
21614          if(Roo.isIE){
21615             this.doc = iframe.contentWindow.document;
21616             this.win = iframe.contentWindow;
21617         } else {
21618 //            if (!Roo.get(this.frameId)) {
21619 //                return;
21620 //            }
21621 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21622 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21623             
21624             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21625                 return;
21626             }
21627             
21628             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21629             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21630         }
21631     },
21632     
21633     // private
21634     initEditor : function(){
21635         //console.log("INIT EDITOR");
21636         this.assignDocWin();
21637         
21638         
21639         
21640         this.doc.designMode="on";
21641         this.doc.open();
21642         this.doc.write(this.getDocMarkup());
21643         this.doc.close();
21644         
21645         var dbody = (this.doc.body || this.doc.documentElement);
21646         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21647         // this copies styles from the containing element into thsi one..
21648         // not sure why we need all of this..
21649         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21650         
21651         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21652         //ss['background-attachment'] = 'fixed'; // w3c
21653         dbody.bgProperties = 'fixed'; // ie
21654         //Roo.DomHelper.applyStyles(dbody, ss);
21655         Roo.EventManager.on(this.doc, {
21656             //'mousedown': this.onEditorEvent,
21657             'mouseup': this.onEditorEvent,
21658             'dblclick': this.onEditorEvent,
21659             'click': this.onEditorEvent,
21660             'keyup': this.onEditorEvent,
21661             buffer:100,
21662             scope: this
21663         });
21664         if(Roo.isGecko){
21665             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21666         }
21667         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21668             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21669         }
21670         this.initialized = true;
21671
21672         this.owner.fireEvent('initialize', this);
21673         this.pushValue();
21674     },
21675
21676     // private
21677     onDestroy : function(){
21678         
21679         
21680         
21681         if(this.rendered){
21682             
21683             //for (var i =0; i < this.toolbars.length;i++) {
21684             //    // fixme - ask toolbars for heights?
21685             //    this.toolbars[i].onDestroy();
21686            // }
21687             
21688             //this.wrap.dom.innerHTML = '';
21689             //this.wrap.remove();
21690         }
21691     },
21692
21693     // private
21694     onFirstFocus : function(){
21695         
21696         this.assignDocWin();
21697         
21698         
21699         this.activated = true;
21700          
21701     
21702         if(Roo.isGecko){ // prevent silly gecko errors
21703             this.win.focus();
21704             var s = this.win.getSelection();
21705             if(!s.focusNode || s.focusNode.nodeType != 3){
21706                 var r = s.getRangeAt(0);
21707                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21708                 r.collapse(true);
21709                 this.deferFocus();
21710             }
21711             try{
21712                 this.execCmd('useCSS', true);
21713                 this.execCmd('styleWithCSS', false);
21714             }catch(e){}
21715         }
21716         this.owner.fireEvent('activate', this);
21717     },
21718
21719     // private
21720     adjustFont: function(btn){
21721         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21722         //if(Roo.isSafari){ // safari
21723         //    adjust *= 2;
21724        // }
21725         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21726         if(Roo.isSafari){ // safari
21727             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21728             v =  (v < 10) ? 10 : v;
21729             v =  (v > 48) ? 48 : v;
21730             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21731             
21732         }
21733         
21734         
21735         v = Math.max(1, v+adjust);
21736         
21737         this.execCmd('FontSize', v  );
21738     },
21739
21740     onEditorEvent : function(e)
21741     {
21742         this.owner.fireEvent('editorevent', this, e);
21743       //  this.updateToolbar();
21744         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21745     },
21746
21747     insertTag : function(tg)
21748     {
21749         // could be a bit smarter... -> wrap the current selected tRoo..
21750         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21751             
21752             range = this.createRange(this.getSelection());
21753             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21754             wrappingNode.appendChild(range.extractContents());
21755             range.insertNode(wrappingNode);
21756
21757             return;
21758             
21759             
21760             
21761         }
21762         this.execCmd("formatblock",   tg);
21763         
21764     },
21765     
21766     insertText : function(txt)
21767     {
21768         
21769         
21770         var range = this.createRange();
21771         range.deleteContents();
21772                //alert(Sender.getAttribute('label'));
21773                
21774         range.insertNode(this.doc.createTextNode(txt));
21775     } ,
21776     
21777      
21778
21779     /**
21780      * Executes a Midas editor command on the editor document and performs necessary focus and
21781      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21782      * @param {String} cmd The Midas command
21783      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21784      */
21785     relayCmd : function(cmd, value){
21786         this.win.focus();
21787         this.execCmd(cmd, value);
21788         this.owner.fireEvent('editorevent', this);
21789         //this.updateToolbar();
21790         this.owner.deferFocus();
21791     },
21792
21793     /**
21794      * Executes a Midas editor command directly on the editor document.
21795      * For visual commands, you should use {@link #relayCmd} instead.
21796      * <b>This should only be called after the editor is initialized.</b>
21797      * @param {String} cmd The Midas command
21798      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21799      */
21800     execCmd : function(cmd, value){
21801         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21802         this.syncValue();
21803     },
21804  
21805  
21806    
21807     /**
21808      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21809      * to insert tRoo.
21810      * @param {String} text | dom node.. 
21811      */
21812     insertAtCursor : function(text)
21813     {
21814         
21815         if(!this.activated){
21816             return;
21817         }
21818         /*
21819         if(Roo.isIE){
21820             this.win.focus();
21821             var r = this.doc.selection.createRange();
21822             if(r){
21823                 r.collapse(true);
21824                 r.pasteHTML(text);
21825                 this.syncValue();
21826                 this.deferFocus();
21827             
21828             }
21829             return;
21830         }
21831         */
21832         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21833             this.win.focus();
21834             
21835             
21836             // from jquery ui (MIT licenced)
21837             var range, node;
21838             var win = this.win;
21839             
21840             if (win.getSelection && win.getSelection().getRangeAt) {
21841                 range = win.getSelection().getRangeAt(0);
21842                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21843                 range.insertNode(node);
21844             } else if (win.document.selection && win.document.selection.createRange) {
21845                 // no firefox support
21846                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21847                 win.document.selection.createRange().pasteHTML(txt);
21848             } else {
21849                 // no firefox support
21850                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21851                 this.execCmd('InsertHTML', txt);
21852             } 
21853             
21854             this.syncValue();
21855             
21856             this.deferFocus();
21857         }
21858     },
21859  // private
21860     mozKeyPress : function(e){
21861         if(e.ctrlKey){
21862             var c = e.getCharCode(), cmd;
21863           
21864             if(c > 0){
21865                 c = String.fromCharCode(c).toLowerCase();
21866                 switch(c){
21867                     case 'b':
21868                         cmd = 'bold';
21869                         break;
21870                     case 'i':
21871                         cmd = 'italic';
21872                         break;
21873                     
21874                     case 'u':
21875                         cmd = 'underline';
21876                         break;
21877                     
21878                     case 'v':
21879                         this.cleanUpPaste.defer(100, this);
21880                         return;
21881                         
21882                 }
21883                 if(cmd){
21884                     this.win.focus();
21885                     this.execCmd(cmd);
21886                     this.deferFocus();
21887                     e.preventDefault();
21888                 }
21889                 
21890             }
21891         }
21892     },
21893
21894     // private
21895     fixKeys : function(){ // load time branching for fastest keydown performance
21896         if(Roo.isIE){
21897             return function(e){
21898                 var k = e.getKey(), r;
21899                 if(k == e.TAB){
21900                     e.stopEvent();
21901                     r = this.doc.selection.createRange();
21902                     if(r){
21903                         r.collapse(true);
21904                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21905                         this.deferFocus();
21906                     }
21907                     return;
21908                 }
21909                 
21910                 if(k == e.ENTER){
21911                     r = this.doc.selection.createRange();
21912                     if(r){
21913                         var target = r.parentElement();
21914                         if(!target || target.tagName.toLowerCase() != 'li'){
21915                             e.stopEvent();
21916                             r.pasteHTML('<br />');
21917                             r.collapse(false);
21918                             r.select();
21919                         }
21920                     }
21921                 }
21922                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21923                     this.cleanUpPaste.defer(100, this);
21924                     return;
21925                 }
21926                 
21927                 
21928             };
21929         }else if(Roo.isOpera){
21930             return function(e){
21931                 var k = e.getKey();
21932                 if(k == e.TAB){
21933                     e.stopEvent();
21934                     this.win.focus();
21935                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21936                     this.deferFocus();
21937                 }
21938                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21939                     this.cleanUpPaste.defer(100, this);
21940                     return;
21941                 }
21942                 
21943             };
21944         }else if(Roo.isSafari){
21945             return function(e){
21946                 var k = e.getKey();
21947                 
21948                 if(k == e.TAB){
21949                     e.stopEvent();
21950                     this.execCmd('InsertText','\t');
21951                     this.deferFocus();
21952                     return;
21953                 }
21954                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21955                     this.cleanUpPaste.defer(100, this);
21956                     return;
21957                 }
21958                 
21959              };
21960         }
21961     }(),
21962     
21963     getAllAncestors: function()
21964     {
21965         var p = this.getSelectedNode();
21966         var a = [];
21967         if (!p) {
21968             a.push(p); // push blank onto stack..
21969             p = this.getParentElement();
21970         }
21971         
21972         
21973         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21974             a.push(p);
21975             p = p.parentNode;
21976         }
21977         a.push(this.doc.body);
21978         return a;
21979     },
21980     lastSel : false,
21981     lastSelNode : false,
21982     
21983     
21984     getSelection : function() 
21985     {
21986         this.assignDocWin();
21987         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21988     },
21989     
21990     getSelectedNode: function() 
21991     {
21992         // this may only work on Gecko!!!
21993         
21994         // should we cache this!!!!
21995         
21996         
21997         
21998          
21999         var range = this.createRange(this.getSelection()).cloneRange();
22000         
22001         if (Roo.isIE) {
22002             var parent = range.parentElement();
22003             while (true) {
22004                 var testRange = range.duplicate();
22005                 testRange.moveToElementText(parent);
22006                 if (testRange.inRange(range)) {
22007                     break;
22008                 }
22009                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22010                     break;
22011                 }
22012                 parent = parent.parentElement;
22013             }
22014             return parent;
22015         }
22016         
22017         // is ancestor a text element.
22018         var ac =  range.commonAncestorContainer;
22019         if (ac.nodeType == 3) {
22020             ac = ac.parentNode;
22021         }
22022         
22023         var ar = ac.childNodes;
22024          
22025         var nodes = [];
22026         var other_nodes = [];
22027         var has_other_nodes = false;
22028         for (var i=0;i<ar.length;i++) {
22029             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22030                 continue;
22031             }
22032             // fullly contained node.
22033             
22034             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22035                 nodes.push(ar[i]);
22036                 continue;
22037             }
22038             
22039             // probably selected..
22040             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22041                 other_nodes.push(ar[i]);
22042                 continue;
22043             }
22044             // outer..
22045             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22046                 continue;
22047             }
22048             
22049             
22050             has_other_nodes = true;
22051         }
22052         if (!nodes.length && other_nodes.length) {
22053             nodes= other_nodes;
22054         }
22055         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22056             return false;
22057         }
22058         
22059         return nodes[0];
22060     },
22061     createRange: function(sel)
22062     {
22063         // this has strange effects when using with 
22064         // top toolbar - not sure if it's a great idea.
22065         //this.editor.contentWindow.focus();
22066         if (typeof sel != "undefined") {
22067             try {
22068                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22069             } catch(e) {
22070                 return this.doc.createRange();
22071             }
22072         } else {
22073             return this.doc.createRange();
22074         }
22075     },
22076     getParentElement: function()
22077     {
22078         
22079         this.assignDocWin();
22080         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22081         
22082         var range = this.createRange(sel);
22083          
22084         try {
22085             var p = range.commonAncestorContainer;
22086             while (p.nodeType == 3) { // text node
22087                 p = p.parentNode;
22088             }
22089             return p;
22090         } catch (e) {
22091             return null;
22092         }
22093     
22094     },
22095     /***
22096      *
22097      * Range intersection.. the hard stuff...
22098      *  '-1' = before
22099      *  '0' = hits..
22100      *  '1' = after.
22101      *         [ -- selected range --- ]
22102      *   [fail]                        [fail]
22103      *
22104      *    basically..
22105      *      if end is before start or  hits it. fail.
22106      *      if start is after end or hits it fail.
22107      *
22108      *   if either hits (but other is outside. - then it's not 
22109      *   
22110      *    
22111      **/
22112     
22113     
22114     // @see http://www.thismuchiknow.co.uk/?p=64.
22115     rangeIntersectsNode : function(range, node)
22116     {
22117         var nodeRange = node.ownerDocument.createRange();
22118         try {
22119             nodeRange.selectNode(node);
22120         } catch (e) {
22121             nodeRange.selectNodeContents(node);
22122         }
22123     
22124         var rangeStartRange = range.cloneRange();
22125         rangeStartRange.collapse(true);
22126     
22127         var rangeEndRange = range.cloneRange();
22128         rangeEndRange.collapse(false);
22129     
22130         var nodeStartRange = nodeRange.cloneRange();
22131         nodeStartRange.collapse(true);
22132     
22133         var nodeEndRange = nodeRange.cloneRange();
22134         nodeEndRange.collapse(false);
22135     
22136         return rangeStartRange.compareBoundaryPoints(
22137                  Range.START_TO_START, nodeEndRange) == -1 &&
22138                rangeEndRange.compareBoundaryPoints(
22139                  Range.START_TO_START, nodeStartRange) == 1;
22140         
22141          
22142     },
22143     rangeCompareNode : function(range, node)
22144     {
22145         var nodeRange = node.ownerDocument.createRange();
22146         try {
22147             nodeRange.selectNode(node);
22148         } catch (e) {
22149             nodeRange.selectNodeContents(node);
22150         }
22151         
22152         
22153         range.collapse(true);
22154     
22155         nodeRange.collapse(true);
22156      
22157         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22158         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22159          
22160         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22161         
22162         var nodeIsBefore   =  ss == 1;
22163         var nodeIsAfter    = ee == -1;
22164         
22165         if (nodeIsBefore && nodeIsAfter) {
22166             return 0; // outer
22167         }
22168         if (!nodeIsBefore && nodeIsAfter) {
22169             return 1; //right trailed.
22170         }
22171         
22172         if (nodeIsBefore && !nodeIsAfter) {
22173             return 2;  // left trailed.
22174         }
22175         // fully contined.
22176         return 3;
22177     },
22178
22179     // private? - in a new class?
22180     cleanUpPaste :  function()
22181     {
22182         // cleans up the whole document..
22183         Roo.log('cleanuppaste');
22184         
22185         this.cleanUpChildren(this.doc.body);
22186         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22187         if (clean != this.doc.body.innerHTML) {
22188             this.doc.body.innerHTML = clean;
22189         }
22190         
22191     },
22192     
22193     cleanWordChars : function(input) {// change the chars to hex code
22194         var he = Roo.HtmlEditorCore;
22195         
22196         var output = input;
22197         Roo.each(he.swapCodes, function(sw) { 
22198             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22199             
22200             output = output.replace(swapper, sw[1]);
22201         });
22202         
22203         return output;
22204     },
22205     
22206     
22207     cleanUpChildren : function (n)
22208     {
22209         if (!n.childNodes.length) {
22210             return;
22211         }
22212         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22213            this.cleanUpChild(n.childNodes[i]);
22214         }
22215     },
22216     
22217     
22218         
22219     
22220     cleanUpChild : function (node)
22221     {
22222         var ed = this;
22223         //console.log(node);
22224         if (node.nodeName == "#text") {
22225             // clean up silly Windows -- stuff?
22226             return; 
22227         }
22228         if (node.nodeName == "#comment") {
22229             node.parentNode.removeChild(node);
22230             // clean up silly Windows -- stuff?
22231             return; 
22232         }
22233         var lcname = node.tagName.toLowerCase();
22234         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22235         // whitelist of tags..
22236         
22237         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22238             // remove node.
22239             node.parentNode.removeChild(node);
22240             return;
22241             
22242         }
22243         
22244         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22245         
22246         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22247         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22248         
22249         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22250         //    remove_keep_children = true;
22251         //}
22252         
22253         if (remove_keep_children) {
22254             this.cleanUpChildren(node);
22255             // inserts everything just before this node...
22256             while (node.childNodes.length) {
22257                 var cn = node.childNodes[0];
22258                 node.removeChild(cn);
22259                 node.parentNode.insertBefore(cn, node);
22260             }
22261             node.parentNode.removeChild(node);
22262             return;
22263         }
22264         
22265         if (!node.attributes || !node.attributes.length) {
22266             this.cleanUpChildren(node);
22267             return;
22268         }
22269         
22270         function cleanAttr(n,v)
22271         {
22272             
22273             if (v.match(/^\./) || v.match(/^\//)) {
22274                 return;
22275             }
22276             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22277                 return;
22278             }
22279             if (v.match(/^#/)) {
22280                 return;
22281             }
22282 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22283             node.removeAttribute(n);
22284             
22285         }
22286         
22287         var cwhite = this.cwhite;
22288         var cblack = this.cblack;
22289             
22290         function cleanStyle(n,v)
22291         {
22292             if (v.match(/expression/)) { //XSS?? should we even bother..
22293                 node.removeAttribute(n);
22294                 return;
22295             }
22296             
22297             var parts = v.split(/;/);
22298             var clean = [];
22299             
22300             Roo.each(parts, function(p) {
22301                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22302                 if (!p.length) {
22303                     return true;
22304                 }
22305                 var l = p.split(':').shift().replace(/\s+/g,'');
22306                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22307                 
22308                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22309 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22310                     //node.removeAttribute(n);
22311                     return true;
22312                 }
22313                 //Roo.log()
22314                 // only allow 'c whitelisted system attributes'
22315                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22316 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22317                     //node.removeAttribute(n);
22318                     return true;
22319                 }
22320                 
22321                 
22322                  
22323                 
22324                 clean.push(p);
22325                 return true;
22326             });
22327             if (clean.length) { 
22328                 node.setAttribute(n, clean.join(';'));
22329             } else {
22330                 node.removeAttribute(n);
22331             }
22332             
22333         }
22334         
22335         
22336         for (var i = node.attributes.length-1; i > -1 ; i--) {
22337             var a = node.attributes[i];
22338             //console.log(a);
22339             
22340             if (a.name.toLowerCase().substr(0,2)=='on')  {
22341                 node.removeAttribute(a.name);
22342                 continue;
22343             }
22344             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22345                 node.removeAttribute(a.name);
22346                 continue;
22347             }
22348             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22349                 cleanAttr(a.name,a.value); // fixme..
22350                 continue;
22351             }
22352             if (a.name == 'style') {
22353                 cleanStyle(a.name,a.value);
22354                 continue;
22355             }
22356             /// clean up MS crap..
22357             // tecnically this should be a list of valid class'es..
22358             
22359             
22360             if (a.name == 'class') {
22361                 if (a.value.match(/^Mso/)) {
22362                     node.className = '';
22363                 }
22364                 
22365                 if (a.value.match(/^body$/)) {
22366                     node.className = '';
22367                 }
22368                 continue;
22369             }
22370             
22371             // style cleanup!?
22372             // class cleanup?
22373             
22374         }
22375         
22376         
22377         this.cleanUpChildren(node);
22378         
22379         
22380     },
22381     
22382     /**
22383      * Clean up MS wordisms...
22384      */
22385     cleanWord : function(node)
22386     {
22387         
22388         
22389         if (!node) {
22390             this.cleanWord(this.doc.body);
22391             return;
22392         }
22393         if (node.nodeName == "#text") {
22394             // clean up silly Windows -- stuff?
22395             return; 
22396         }
22397         if (node.nodeName == "#comment") {
22398             node.parentNode.removeChild(node);
22399             // clean up silly Windows -- stuff?
22400             return; 
22401         }
22402         
22403         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22404             node.parentNode.removeChild(node);
22405             return;
22406         }
22407         
22408         // remove - but keep children..
22409         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22410             while (node.childNodes.length) {
22411                 var cn = node.childNodes[0];
22412                 node.removeChild(cn);
22413                 node.parentNode.insertBefore(cn, node);
22414             }
22415             node.parentNode.removeChild(node);
22416             this.iterateChildren(node, this.cleanWord);
22417             return;
22418         }
22419         // clean styles
22420         if (node.className.length) {
22421             
22422             var cn = node.className.split(/\W+/);
22423             var cna = [];
22424             Roo.each(cn, function(cls) {
22425                 if (cls.match(/Mso[a-zA-Z]+/)) {
22426                     return;
22427                 }
22428                 cna.push(cls);
22429             });
22430             node.className = cna.length ? cna.join(' ') : '';
22431             if (!cna.length) {
22432                 node.removeAttribute("class");
22433             }
22434         }
22435         
22436         if (node.hasAttribute("lang")) {
22437             node.removeAttribute("lang");
22438         }
22439         
22440         if (node.hasAttribute("style")) {
22441             
22442             var styles = node.getAttribute("style").split(";");
22443             var nstyle = [];
22444             Roo.each(styles, function(s) {
22445                 if (!s.match(/:/)) {
22446                     return;
22447                 }
22448                 var kv = s.split(":");
22449                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22450                     return;
22451                 }
22452                 // what ever is left... we allow.
22453                 nstyle.push(s);
22454             });
22455             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22456             if (!nstyle.length) {
22457                 node.removeAttribute('style');
22458             }
22459         }
22460         this.iterateChildren(node, this.cleanWord);
22461         
22462         
22463         
22464     },
22465     /**
22466      * iterateChildren of a Node, calling fn each time, using this as the scole..
22467      * @param {DomNode} node node to iterate children of.
22468      * @param {Function} fn method of this class to call on each item.
22469      */
22470     iterateChildren : function(node, fn)
22471     {
22472         if (!node.childNodes.length) {
22473                 return;
22474         }
22475         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22476            fn.call(this, node.childNodes[i])
22477         }
22478     },
22479     
22480     
22481     /**
22482      * cleanTableWidths.
22483      *
22484      * Quite often pasting from word etc.. results in tables with column and widths.
22485      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22486      *
22487      */
22488     cleanTableWidths : function(node)
22489     {
22490          
22491          
22492         if (!node) {
22493             this.cleanTableWidths(this.doc.body);
22494             return;
22495         }
22496         
22497         // ignore list...
22498         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22499             return; 
22500         }
22501         Roo.log(node.tagName);
22502         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22503             this.iterateChildren(node, this.cleanTableWidths);
22504             return;
22505         }
22506         if (node.hasAttribute('width')) {
22507             node.removeAttribute('width');
22508         }
22509         
22510          
22511         if (node.hasAttribute("style")) {
22512             // pretty basic...
22513             
22514             var styles = node.getAttribute("style").split(";");
22515             var nstyle = [];
22516             Roo.each(styles, function(s) {
22517                 if (!s.match(/:/)) {
22518                     return;
22519                 }
22520                 var kv = s.split(":");
22521                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22522                     return;
22523                 }
22524                 // what ever is left... we allow.
22525                 nstyle.push(s);
22526             });
22527             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22528             if (!nstyle.length) {
22529                 node.removeAttribute('style');
22530             }
22531         }
22532         
22533         this.iterateChildren(node, this.cleanTableWidths);
22534         
22535         
22536     },
22537     
22538     
22539     
22540     
22541     domToHTML : function(currentElement, depth, nopadtext) {
22542         
22543         depth = depth || 0;
22544         nopadtext = nopadtext || false;
22545     
22546         if (!currentElement) {
22547             return this.domToHTML(this.doc.body);
22548         }
22549         
22550         //Roo.log(currentElement);
22551         var j;
22552         var allText = false;
22553         var nodeName = currentElement.nodeName;
22554         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22555         
22556         if  (nodeName == '#text') {
22557             
22558             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22559         }
22560         
22561         
22562         var ret = '';
22563         if (nodeName != 'BODY') {
22564              
22565             var i = 0;
22566             // Prints the node tagName, such as <A>, <IMG>, etc
22567             if (tagName) {
22568                 var attr = [];
22569                 for(i = 0; i < currentElement.attributes.length;i++) {
22570                     // quoting?
22571                     var aname = currentElement.attributes.item(i).name;
22572                     if (!currentElement.attributes.item(i).value.length) {
22573                         continue;
22574                     }
22575                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22576                 }
22577                 
22578                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22579             } 
22580             else {
22581                 
22582                 // eack
22583             }
22584         } else {
22585             tagName = false;
22586         }
22587         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22588             return ret;
22589         }
22590         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22591             nopadtext = true;
22592         }
22593         
22594         
22595         // Traverse the tree
22596         i = 0;
22597         var currentElementChild = currentElement.childNodes.item(i);
22598         var allText = true;
22599         var innerHTML  = '';
22600         lastnode = '';
22601         while (currentElementChild) {
22602             // Formatting code (indent the tree so it looks nice on the screen)
22603             var nopad = nopadtext;
22604             if (lastnode == 'SPAN') {
22605                 nopad  = true;
22606             }
22607             // text
22608             if  (currentElementChild.nodeName == '#text') {
22609                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22610                 toadd = nopadtext ? toadd : toadd.trim();
22611                 if (!nopad && toadd.length > 80) {
22612                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22613                 }
22614                 innerHTML  += toadd;
22615                 
22616                 i++;
22617                 currentElementChild = currentElement.childNodes.item(i);
22618                 lastNode = '';
22619                 continue;
22620             }
22621             allText = false;
22622             
22623             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22624                 
22625             // Recursively traverse the tree structure of the child node
22626             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22627             lastnode = currentElementChild.nodeName;
22628             i++;
22629             currentElementChild=currentElement.childNodes.item(i);
22630         }
22631         
22632         ret += innerHTML;
22633         
22634         if (!allText) {
22635                 // The remaining code is mostly for formatting the tree
22636             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22637         }
22638         
22639         
22640         if (tagName) {
22641             ret+= "</"+tagName+">";
22642         }
22643         return ret;
22644         
22645     },
22646         
22647     applyBlacklists : function()
22648     {
22649         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22650         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22651         
22652         this.white = [];
22653         this.black = [];
22654         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22655             if (b.indexOf(tag) > -1) {
22656                 return;
22657             }
22658             this.white.push(tag);
22659             
22660         }, this);
22661         
22662         Roo.each(w, function(tag) {
22663             if (b.indexOf(tag) > -1) {
22664                 return;
22665             }
22666             if (this.white.indexOf(tag) > -1) {
22667                 return;
22668             }
22669             this.white.push(tag);
22670             
22671         }, this);
22672         
22673         
22674         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22675             if (w.indexOf(tag) > -1) {
22676                 return;
22677             }
22678             this.black.push(tag);
22679             
22680         }, this);
22681         
22682         Roo.each(b, function(tag) {
22683             if (w.indexOf(tag) > -1) {
22684                 return;
22685             }
22686             if (this.black.indexOf(tag) > -1) {
22687                 return;
22688             }
22689             this.black.push(tag);
22690             
22691         }, this);
22692         
22693         
22694         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22695         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22696         
22697         this.cwhite = [];
22698         this.cblack = [];
22699         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22700             if (b.indexOf(tag) > -1) {
22701                 return;
22702             }
22703             this.cwhite.push(tag);
22704             
22705         }, this);
22706         
22707         Roo.each(w, function(tag) {
22708             if (b.indexOf(tag) > -1) {
22709                 return;
22710             }
22711             if (this.cwhite.indexOf(tag) > -1) {
22712                 return;
22713             }
22714             this.cwhite.push(tag);
22715             
22716         }, this);
22717         
22718         
22719         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22720             if (w.indexOf(tag) > -1) {
22721                 return;
22722             }
22723             this.cblack.push(tag);
22724             
22725         }, this);
22726         
22727         Roo.each(b, function(tag) {
22728             if (w.indexOf(tag) > -1) {
22729                 return;
22730             }
22731             if (this.cblack.indexOf(tag) > -1) {
22732                 return;
22733             }
22734             this.cblack.push(tag);
22735             
22736         }, this);
22737     },
22738     
22739     setStylesheets : function(stylesheets)
22740     {
22741         if(typeof(stylesheets) == 'string'){
22742             Roo.get(this.iframe.contentDocument.head).createChild({
22743                 tag : 'link',
22744                 rel : 'stylesheet',
22745                 type : 'text/css',
22746                 href : stylesheets
22747             });
22748             
22749             return;
22750         }
22751         var _this = this;
22752      
22753         Roo.each(stylesheets, function(s) {
22754             if(!s.length){
22755                 return;
22756             }
22757             
22758             Roo.get(_this.iframe.contentDocument.head).createChild({
22759                 tag : 'link',
22760                 rel : 'stylesheet',
22761                 type : 'text/css',
22762                 href : s
22763             });
22764         });
22765
22766         
22767     },
22768     
22769     removeStylesheets : function()
22770     {
22771         var _this = this;
22772         
22773         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22774             s.remove();
22775         });
22776     },
22777     
22778     setStyle : function(style)
22779     {
22780         Roo.get(this.iframe.contentDocument.head).createChild({
22781             tag : 'style',
22782             type : 'text/css',
22783             html : style
22784         });
22785
22786         return;
22787     }
22788     
22789     // hide stuff that is not compatible
22790     /**
22791      * @event blur
22792      * @hide
22793      */
22794     /**
22795      * @event change
22796      * @hide
22797      */
22798     /**
22799      * @event focus
22800      * @hide
22801      */
22802     /**
22803      * @event specialkey
22804      * @hide
22805      */
22806     /**
22807      * @cfg {String} fieldClass @hide
22808      */
22809     /**
22810      * @cfg {String} focusClass @hide
22811      */
22812     /**
22813      * @cfg {String} autoCreate @hide
22814      */
22815     /**
22816      * @cfg {String} inputType @hide
22817      */
22818     /**
22819      * @cfg {String} invalidClass @hide
22820      */
22821     /**
22822      * @cfg {String} invalidText @hide
22823      */
22824     /**
22825      * @cfg {String} msgFx @hide
22826      */
22827     /**
22828      * @cfg {String} validateOnBlur @hide
22829      */
22830 });
22831
22832 Roo.HtmlEditorCore.white = [
22833         'area', 'br', 'img', 'input', 'hr', 'wbr',
22834         
22835        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22836        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22837        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22838        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22839        'table',   'ul',         'xmp', 
22840        
22841        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22842       'thead',   'tr', 
22843      
22844       'dir', 'menu', 'ol', 'ul', 'dl',
22845        
22846       'embed',  'object'
22847 ];
22848
22849
22850 Roo.HtmlEditorCore.black = [
22851     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22852         'applet', // 
22853         'base',   'basefont', 'bgsound', 'blink',  'body', 
22854         'frame',  'frameset', 'head',    'html',   'ilayer', 
22855         'iframe', 'layer',  'link',     'meta',    'object',   
22856         'script', 'style' ,'title',  'xml' // clean later..
22857 ];
22858 Roo.HtmlEditorCore.clean = [
22859     'script', 'style', 'title', 'xml'
22860 ];
22861 Roo.HtmlEditorCore.remove = [
22862     'font'
22863 ];
22864 // attributes..
22865
22866 Roo.HtmlEditorCore.ablack = [
22867     'on'
22868 ];
22869     
22870 Roo.HtmlEditorCore.aclean = [ 
22871     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22872 ];
22873
22874 // protocols..
22875 Roo.HtmlEditorCore.pwhite= [
22876         'http',  'https',  'mailto'
22877 ];
22878
22879 // white listed style attributes.
22880 Roo.HtmlEditorCore.cwhite= [
22881       //  'text-align', /// default is to allow most things..
22882       
22883          
22884 //        'font-size'//??
22885 ];
22886
22887 // black listed style attributes.
22888 Roo.HtmlEditorCore.cblack= [
22889       //  'font-size' -- this can be set by the project 
22890 ];
22891
22892
22893 Roo.HtmlEditorCore.swapCodes   =[ 
22894     [    8211, "--" ], 
22895     [    8212, "--" ], 
22896     [    8216,  "'" ],  
22897     [    8217, "'" ],  
22898     [    8220, '"' ],  
22899     [    8221, '"' ],  
22900     [    8226, "*" ],  
22901     [    8230, "..." ]
22902 ]; 
22903
22904     /*
22905  * - LGPL
22906  *
22907  * HtmlEditor
22908  * 
22909  */
22910
22911 /**
22912  * @class Roo.bootstrap.HtmlEditor
22913  * @extends Roo.bootstrap.TextArea
22914  * Bootstrap HtmlEditor class
22915
22916  * @constructor
22917  * Create a new HtmlEditor
22918  * @param {Object} config The config object
22919  */
22920
22921 Roo.bootstrap.HtmlEditor = function(config){
22922     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22923     if (!this.toolbars) {
22924         this.toolbars = [];
22925     }
22926     
22927     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22928     this.addEvents({
22929             /**
22930              * @event initialize
22931              * Fires when the editor is fully initialized (including the iframe)
22932              * @param {HtmlEditor} this
22933              */
22934             initialize: true,
22935             /**
22936              * @event activate
22937              * Fires when the editor is first receives the focus. Any insertion must wait
22938              * until after this event.
22939              * @param {HtmlEditor} this
22940              */
22941             activate: true,
22942              /**
22943              * @event beforesync
22944              * Fires before the textarea is updated with content from the editor iframe. Return false
22945              * to cancel the sync.
22946              * @param {HtmlEditor} this
22947              * @param {String} html
22948              */
22949             beforesync: true,
22950              /**
22951              * @event beforepush
22952              * Fires before the iframe editor is updated with content from the textarea. Return false
22953              * to cancel the push.
22954              * @param {HtmlEditor} this
22955              * @param {String} html
22956              */
22957             beforepush: true,
22958              /**
22959              * @event sync
22960              * Fires when the textarea is updated with content from the editor iframe.
22961              * @param {HtmlEditor} this
22962              * @param {String} html
22963              */
22964             sync: true,
22965              /**
22966              * @event push
22967              * Fires when the iframe editor is updated with content from the textarea.
22968              * @param {HtmlEditor} this
22969              * @param {String} html
22970              */
22971             push: true,
22972              /**
22973              * @event editmodechange
22974              * Fires when the editor switches edit modes
22975              * @param {HtmlEditor} this
22976              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22977              */
22978             editmodechange: true,
22979             /**
22980              * @event editorevent
22981              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22982              * @param {HtmlEditor} this
22983              */
22984             editorevent: true,
22985             /**
22986              * @event firstfocus
22987              * Fires when on first focus - needed by toolbars..
22988              * @param {HtmlEditor} this
22989              */
22990             firstfocus: true,
22991             /**
22992              * @event autosave
22993              * Auto save the htmlEditor value as a file into Events
22994              * @param {HtmlEditor} this
22995              */
22996             autosave: true,
22997             /**
22998              * @event savedpreview
22999              * preview the saved version of htmlEditor
23000              * @param {HtmlEditor} this
23001              */
23002             savedpreview: true
23003         });
23004 };
23005
23006
23007 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23008     
23009     
23010       /**
23011      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23012      */
23013     toolbars : false,
23014     
23015      /**
23016     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23017     */
23018     btns : [],
23019    
23020      /**
23021      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23022      *                        Roo.resizable.
23023      */
23024     resizable : false,
23025      /**
23026      * @cfg {Number} height (in pixels)
23027      */   
23028     height: 300,
23029    /**
23030      * @cfg {Number} width (in pixels)
23031      */   
23032     width: false,
23033     
23034     /**
23035      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23036      * 
23037      */
23038     stylesheets: false,
23039     
23040     // id of frame..
23041     frameId: false,
23042     
23043     // private properties
23044     validationEvent : false,
23045     deferHeight: true,
23046     initialized : false,
23047     activated : false,
23048     
23049     onFocus : Roo.emptyFn,
23050     iframePad:3,
23051     hideMode:'offsets',
23052     
23053     tbContainer : false,
23054     
23055     bodyCls : '',
23056     
23057     toolbarContainer :function() {
23058         return this.wrap.select('.x-html-editor-tb',true).first();
23059     },
23060
23061     /**
23062      * Protected method that will not generally be called directly. It
23063      * is called when the editor creates its toolbar. Override this method if you need to
23064      * add custom toolbar buttons.
23065      * @param {HtmlEditor} editor
23066      */
23067     createToolbar : function(){
23068         Roo.log('renewing');
23069         Roo.log("create toolbars");
23070         
23071         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23072         this.toolbars[0].render(this.toolbarContainer());
23073         
23074         return;
23075         
23076 //        if (!editor.toolbars || !editor.toolbars.length) {
23077 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23078 //        }
23079 //        
23080 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23081 //            editor.toolbars[i] = Roo.factory(
23082 //                    typeof(editor.toolbars[i]) == 'string' ?
23083 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23084 //                Roo.bootstrap.HtmlEditor);
23085 //            editor.toolbars[i].init(editor);
23086 //        }
23087     },
23088
23089      
23090     // private
23091     onRender : function(ct, position)
23092     {
23093        // Roo.log("Call onRender: " + this.xtype);
23094         var _t = this;
23095         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23096       
23097         this.wrap = this.inputEl().wrap({
23098             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23099         });
23100         
23101         this.editorcore.onRender(ct, position);
23102          
23103         if (this.resizable) {
23104             this.resizeEl = new Roo.Resizable(this.wrap, {
23105                 pinned : true,
23106                 wrap: true,
23107                 dynamic : true,
23108                 minHeight : this.height,
23109                 height: this.height,
23110                 handles : this.resizable,
23111                 width: this.width,
23112                 listeners : {
23113                     resize : function(r, w, h) {
23114                         _t.onResize(w,h); // -something
23115                     }
23116                 }
23117             });
23118             
23119         }
23120         this.createToolbar(this);
23121        
23122         
23123         if(!this.width && this.resizable){
23124             this.setSize(this.wrap.getSize());
23125         }
23126         if (this.resizeEl) {
23127             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23128             // should trigger onReize..
23129         }
23130         
23131     },
23132
23133     // private
23134     onResize : function(w, h)
23135     {
23136         Roo.log('resize: ' +w + ',' + h );
23137         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23138         var ew = false;
23139         var eh = false;
23140         
23141         if(this.inputEl() ){
23142             if(typeof w == 'number'){
23143                 var aw = w - this.wrap.getFrameWidth('lr');
23144                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23145                 ew = aw;
23146             }
23147             if(typeof h == 'number'){
23148                  var tbh = -11;  // fixme it needs to tool bar size!
23149                 for (var i =0; i < this.toolbars.length;i++) {
23150                     // fixme - ask toolbars for heights?
23151                     tbh += this.toolbars[i].el.getHeight();
23152                     //if (this.toolbars[i].footer) {
23153                     //    tbh += this.toolbars[i].footer.el.getHeight();
23154                     //}
23155                 }
23156               
23157                 
23158                 
23159                 
23160                 
23161                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23162                 ah -= 5; // knock a few pixes off for look..
23163                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23164                 var eh = ah;
23165             }
23166         }
23167         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23168         this.editorcore.onResize(ew,eh);
23169         
23170     },
23171
23172     /**
23173      * Toggles the editor between standard and source edit mode.
23174      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23175      */
23176     toggleSourceEdit : function(sourceEditMode)
23177     {
23178         this.editorcore.toggleSourceEdit(sourceEditMode);
23179         
23180         if(this.editorcore.sourceEditMode){
23181             Roo.log('editor - showing textarea');
23182             
23183 //            Roo.log('in');
23184 //            Roo.log(this.syncValue());
23185             this.syncValue();
23186             this.inputEl().removeClass(['hide', 'x-hidden']);
23187             this.inputEl().dom.removeAttribute('tabIndex');
23188             this.inputEl().focus();
23189         }else{
23190             Roo.log('editor - hiding textarea');
23191 //            Roo.log('out')
23192 //            Roo.log(this.pushValue()); 
23193             this.pushValue();
23194             
23195             this.inputEl().addClass(['hide', 'x-hidden']);
23196             this.inputEl().dom.setAttribute('tabIndex', -1);
23197             //this.deferFocus();
23198         }
23199          
23200         if(this.resizable){
23201             this.setSize(this.wrap.getSize());
23202         }
23203         
23204         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23205     },
23206  
23207     // private (for BoxComponent)
23208     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23209
23210     // private (for BoxComponent)
23211     getResizeEl : function(){
23212         return this.wrap;
23213     },
23214
23215     // private (for BoxComponent)
23216     getPositionEl : function(){
23217         return this.wrap;
23218     },
23219
23220     // private
23221     initEvents : function(){
23222         this.originalValue = this.getValue();
23223     },
23224
23225 //    /**
23226 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23227 //     * @method
23228 //     */
23229 //    markInvalid : Roo.emptyFn,
23230 //    /**
23231 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23232 //     * @method
23233 //     */
23234 //    clearInvalid : Roo.emptyFn,
23235
23236     setValue : function(v){
23237         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23238         this.editorcore.pushValue();
23239     },
23240
23241      
23242     // private
23243     deferFocus : function(){
23244         this.focus.defer(10, this);
23245     },
23246
23247     // doc'ed in Field
23248     focus : function(){
23249         this.editorcore.focus();
23250         
23251     },
23252       
23253
23254     // private
23255     onDestroy : function(){
23256         
23257         
23258         
23259         if(this.rendered){
23260             
23261             for (var i =0; i < this.toolbars.length;i++) {
23262                 // fixme - ask toolbars for heights?
23263                 this.toolbars[i].onDestroy();
23264             }
23265             
23266             this.wrap.dom.innerHTML = '';
23267             this.wrap.remove();
23268         }
23269     },
23270
23271     // private
23272     onFirstFocus : function(){
23273         //Roo.log("onFirstFocus");
23274         this.editorcore.onFirstFocus();
23275          for (var i =0; i < this.toolbars.length;i++) {
23276             this.toolbars[i].onFirstFocus();
23277         }
23278         
23279     },
23280     
23281     // private
23282     syncValue : function()
23283     {   
23284         this.editorcore.syncValue();
23285     },
23286     
23287     pushValue : function()
23288     {   
23289         this.editorcore.pushValue();
23290     }
23291      
23292     
23293     // hide stuff that is not compatible
23294     /**
23295      * @event blur
23296      * @hide
23297      */
23298     /**
23299      * @event change
23300      * @hide
23301      */
23302     /**
23303      * @event focus
23304      * @hide
23305      */
23306     /**
23307      * @event specialkey
23308      * @hide
23309      */
23310     /**
23311      * @cfg {String} fieldClass @hide
23312      */
23313     /**
23314      * @cfg {String} focusClass @hide
23315      */
23316     /**
23317      * @cfg {String} autoCreate @hide
23318      */
23319     /**
23320      * @cfg {String} inputType @hide
23321      */
23322     /**
23323      * @cfg {String} invalidClass @hide
23324      */
23325     /**
23326      * @cfg {String} invalidText @hide
23327      */
23328     /**
23329      * @cfg {String} msgFx @hide
23330      */
23331     /**
23332      * @cfg {String} validateOnBlur @hide
23333      */
23334 });
23335  
23336     
23337    
23338    
23339    
23340       
23341 Roo.namespace('Roo.bootstrap.htmleditor');
23342 /**
23343  * @class Roo.bootstrap.HtmlEditorToolbar1
23344  * Basic Toolbar
23345  * 
23346  * Usage:
23347  *
23348  new Roo.bootstrap.HtmlEditor({
23349     ....
23350     toolbars : [
23351         new Roo.bootstrap.HtmlEditorToolbar1({
23352             disable : { fonts: 1 , format: 1, ..., ... , ...],
23353             btns : [ .... ]
23354         })
23355     }
23356      
23357  * 
23358  * @cfg {Object} disable List of elements to disable..
23359  * @cfg {Array} btns List of additional buttons.
23360  * 
23361  * 
23362  * NEEDS Extra CSS? 
23363  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23364  */
23365  
23366 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23367 {
23368     
23369     Roo.apply(this, config);
23370     
23371     // default disabled, based on 'good practice'..
23372     this.disable = this.disable || {};
23373     Roo.applyIf(this.disable, {
23374         fontSize : true,
23375         colors : true,
23376         specialElements : true
23377     });
23378     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23379     
23380     this.editor = config.editor;
23381     this.editorcore = config.editor.editorcore;
23382     
23383     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23384     
23385     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23386     // dont call parent... till later.
23387 }
23388 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23389      
23390     bar : true,
23391     
23392     editor : false,
23393     editorcore : false,
23394     
23395     
23396     formats : [
23397         "p" ,  
23398         "h1","h2","h3","h4","h5","h6", 
23399         "pre", "code", 
23400         "abbr", "acronym", "address", "cite", "samp", "var",
23401         'div','span'
23402     ],
23403     
23404     onRender : function(ct, position)
23405     {
23406        // Roo.log("Call onRender: " + this.xtype);
23407         
23408        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23409        Roo.log(this.el);
23410        this.el.dom.style.marginBottom = '0';
23411        var _this = this;
23412        var editorcore = this.editorcore;
23413        var editor= this.editor;
23414        
23415        var children = [];
23416        var btn = function(id,cmd , toggle, handler, html){
23417        
23418             var  event = toggle ? 'toggle' : 'click';
23419        
23420             var a = {
23421                 size : 'sm',
23422                 xtype: 'Button',
23423                 xns: Roo.bootstrap,
23424                 glyphicon : id,
23425                 cmd : id || cmd,
23426                 enableToggle:toggle !== false,
23427                 html : html || '',
23428                 pressed : toggle ? false : null,
23429                 listeners : {}
23430             };
23431             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23432                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23433             };
23434             children.push(a);
23435             return a;
23436        }
23437        
23438     //    var cb_box = function...
23439         
23440         var style = {
23441                 xtype: 'Button',
23442                 size : 'sm',
23443                 xns: Roo.bootstrap,
23444                 glyphicon : 'font',
23445                 //html : 'submit'
23446                 menu : {
23447                     xtype: 'Menu',
23448                     xns: Roo.bootstrap,
23449                     items:  []
23450                 }
23451         };
23452         Roo.each(this.formats, function(f) {
23453             style.menu.items.push({
23454                 xtype :'MenuItem',
23455                 xns: Roo.bootstrap,
23456                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23457                 tagname : f,
23458                 listeners : {
23459                     click : function()
23460                     {
23461                         editorcore.insertTag(this.tagname);
23462                         editor.focus();
23463                     }
23464                 }
23465                 
23466             });
23467         });
23468         children.push(style);   
23469         
23470         btn('bold',false,true);
23471         btn('italic',false,true);
23472         btn('align-left', 'justifyleft',true);
23473         btn('align-center', 'justifycenter',true);
23474         btn('align-right' , 'justifyright',true);
23475         btn('link', false, false, function(btn) {
23476             //Roo.log("create link?");
23477             var url = prompt(this.createLinkText, this.defaultLinkValue);
23478             if(url && url != 'http:/'+'/'){
23479                 this.editorcore.relayCmd('createlink', url);
23480             }
23481         }),
23482         btn('list','insertunorderedlist',true);
23483         btn('pencil', false,true, function(btn){
23484                 Roo.log(this);
23485                 this.toggleSourceEdit(btn.pressed);
23486         });
23487         
23488         if (this.editor.btns.length > 0) {
23489             for (var i = 0; i<this.editor.btns.length; i++) {
23490                 children.push(this.editor.btns[i]);
23491             }
23492         }
23493         
23494         /*
23495         var cog = {
23496                 xtype: 'Button',
23497                 size : 'sm',
23498                 xns: Roo.bootstrap,
23499                 glyphicon : 'cog',
23500                 //html : 'submit'
23501                 menu : {
23502                     xtype: 'Menu',
23503                     xns: Roo.bootstrap,
23504                     items:  []
23505                 }
23506         };
23507         
23508         cog.menu.items.push({
23509             xtype :'MenuItem',
23510             xns: Roo.bootstrap,
23511             html : Clean styles,
23512             tagname : f,
23513             listeners : {
23514                 click : function()
23515                 {
23516                     editorcore.insertTag(this.tagname);
23517                     editor.focus();
23518                 }
23519             }
23520             
23521         });
23522        */
23523         
23524          
23525        this.xtype = 'NavSimplebar';
23526         
23527         for(var i=0;i< children.length;i++) {
23528             
23529             this.buttons.add(this.addxtypeChild(children[i]));
23530             
23531         }
23532         
23533         editor.on('editorevent', this.updateToolbar, this);
23534     },
23535     onBtnClick : function(id)
23536     {
23537        this.editorcore.relayCmd(id);
23538        this.editorcore.focus();
23539     },
23540     
23541     /**
23542      * Protected method that will not generally be called directly. It triggers
23543      * a toolbar update by reading the markup state of the current selection in the editor.
23544      */
23545     updateToolbar: function(){
23546
23547         if(!this.editorcore.activated){
23548             this.editor.onFirstFocus(); // is this neeed?
23549             return;
23550         }
23551
23552         var btns = this.buttons; 
23553         var doc = this.editorcore.doc;
23554         btns.get('bold').setActive(doc.queryCommandState('bold'));
23555         btns.get('italic').setActive(doc.queryCommandState('italic'));
23556         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23557         
23558         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23559         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23560         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23561         
23562         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23563         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23564          /*
23565         
23566         var ans = this.editorcore.getAllAncestors();
23567         if (this.formatCombo) {
23568             
23569             
23570             var store = this.formatCombo.store;
23571             this.formatCombo.setValue("");
23572             for (var i =0; i < ans.length;i++) {
23573                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23574                     // select it..
23575                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23576                     break;
23577                 }
23578             }
23579         }
23580         
23581         
23582         
23583         // hides menus... - so this cant be on a menu...
23584         Roo.bootstrap.MenuMgr.hideAll();
23585         */
23586         Roo.bootstrap.MenuMgr.hideAll();
23587         //this.editorsyncValue();
23588     },
23589     onFirstFocus: function() {
23590         this.buttons.each(function(item){
23591            item.enable();
23592         });
23593     },
23594     toggleSourceEdit : function(sourceEditMode){
23595         
23596           
23597         if(sourceEditMode){
23598             Roo.log("disabling buttons");
23599            this.buttons.each( function(item){
23600                 if(item.cmd != 'pencil'){
23601                     item.disable();
23602                 }
23603             });
23604           
23605         }else{
23606             Roo.log("enabling buttons");
23607             if(this.editorcore.initialized){
23608                 this.buttons.each( function(item){
23609                     item.enable();
23610                 });
23611             }
23612             
23613         }
23614         Roo.log("calling toggole on editor");
23615         // tell the editor that it's been pressed..
23616         this.editor.toggleSourceEdit(sourceEditMode);
23617        
23618     }
23619 });
23620
23621
23622
23623
23624
23625 /**
23626  * @class Roo.bootstrap.Table.AbstractSelectionModel
23627  * @extends Roo.util.Observable
23628  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23629  * implemented by descendant classes.  This class should not be directly instantiated.
23630  * @constructor
23631  */
23632 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23633     this.locked = false;
23634     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23635 };
23636
23637
23638 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23639     /** @ignore Called by the grid automatically. Do not call directly. */
23640     init : function(grid){
23641         this.grid = grid;
23642         this.initEvents();
23643     },
23644
23645     /**
23646      * Locks the selections.
23647      */
23648     lock : function(){
23649         this.locked = true;
23650     },
23651
23652     /**
23653      * Unlocks the selections.
23654      */
23655     unlock : function(){
23656         this.locked = false;
23657     },
23658
23659     /**
23660      * Returns true if the selections are locked.
23661      * @return {Boolean}
23662      */
23663     isLocked : function(){
23664         return this.locked;
23665     }
23666 });
23667 /**
23668  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23669  * @class Roo.bootstrap.Table.RowSelectionModel
23670  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23671  * It supports multiple selections and keyboard selection/navigation. 
23672  * @constructor
23673  * @param {Object} config
23674  */
23675
23676 Roo.bootstrap.Table.RowSelectionModel = function(config){
23677     Roo.apply(this, config);
23678     this.selections = new Roo.util.MixedCollection(false, function(o){
23679         return o.id;
23680     });
23681
23682     this.last = false;
23683     this.lastActive = false;
23684
23685     this.addEvents({
23686         /**
23687              * @event selectionchange
23688              * Fires when the selection changes
23689              * @param {SelectionModel} this
23690              */
23691             "selectionchange" : true,
23692         /**
23693              * @event afterselectionchange
23694              * Fires after the selection changes (eg. by key press or clicking)
23695              * @param {SelectionModel} this
23696              */
23697             "afterselectionchange" : true,
23698         /**
23699              * @event beforerowselect
23700              * Fires when a row is selected being selected, return false to cancel.
23701              * @param {SelectionModel} this
23702              * @param {Number} rowIndex The selected index
23703              * @param {Boolean} keepExisting False if other selections will be cleared
23704              */
23705             "beforerowselect" : true,
23706         /**
23707              * @event rowselect
23708              * Fires when a row is selected.
23709              * @param {SelectionModel} this
23710              * @param {Number} rowIndex The selected index
23711              * @param {Roo.data.Record} r The record
23712              */
23713             "rowselect" : true,
23714         /**
23715              * @event rowdeselect
23716              * Fires when a row is deselected.
23717              * @param {SelectionModel} this
23718              * @param {Number} rowIndex The selected index
23719              */
23720         "rowdeselect" : true
23721     });
23722     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23723     this.locked = false;
23724  };
23725
23726 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23727     /**
23728      * @cfg {Boolean} singleSelect
23729      * True to allow selection of only one row at a time (defaults to false)
23730      */
23731     singleSelect : false,
23732
23733     // private
23734     initEvents : function()
23735     {
23736
23737         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23738         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23739         //}else{ // allow click to work like normal
23740          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23741         //}
23742         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23743         this.grid.on("rowclick", this.handleMouseDown, this);
23744         
23745         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23746             "up" : function(e){
23747                 if(!e.shiftKey){
23748                     this.selectPrevious(e.shiftKey);
23749                 }else if(this.last !== false && this.lastActive !== false){
23750                     var last = this.last;
23751                     this.selectRange(this.last,  this.lastActive-1);
23752                     this.grid.getView().focusRow(this.lastActive);
23753                     if(last !== false){
23754                         this.last = last;
23755                     }
23756                 }else{
23757                     this.selectFirstRow();
23758                 }
23759                 this.fireEvent("afterselectionchange", this);
23760             },
23761             "down" : function(e){
23762                 if(!e.shiftKey){
23763                     this.selectNext(e.shiftKey);
23764                 }else if(this.last !== false && this.lastActive !== false){
23765                     var last = this.last;
23766                     this.selectRange(this.last,  this.lastActive+1);
23767                     this.grid.getView().focusRow(this.lastActive);
23768                     if(last !== false){
23769                         this.last = last;
23770                     }
23771                 }else{
23772                     this.selectFirstRow();
23773                 }
23774                 this.fireEvent("afterselectionchange", this);
23775             },
23776             scope: this
23777         });
23778         this.grid.store.on('load', function(){
23779             this.selections.clear();
23780         },this);
23781         /*
23782         var view = this.grid.view;
23783         view.on("refresh", this.onRefresh, this);
23784         view.on("rowupdated", this.onRowUpdated, this);
23785         view.on("rowremoved", this.onRemove, this);
23786         */
23787     },
23788
23789     // private
23790     onRefresh : function()
23791     {
23792         var ds = this.grid.store, i, v = this.grid.view;
23793         var s = this.selections;
23794         s.each(function(r){
23795             if((i = ds.indexOfId(r.id)) != -1){
23796                 v.onRowSelect(i);
23797             }else{
23798                 s.remove(r);
23799             }
23800         });
23801     },
23802
23803     // private
23804     onRemove : function(v, index, r){
23805         this.selections.remove(r);
23806     },
23807
23808     // private
23809     onRowUpdated : function(v, index, r){
23810         if(this.isSelected(r)){
23811             v.onRowSelect(index);
23812         }
23813     },
23814
23815     /**
23816      * Select records.
23817      * @param {Array} records The records to select
23818      * @param {Boolean} keepExisting (optional) True to keep existing selections
23819      */
23820     selectRecords : function(records, keepExisting)
23821     {
23822         if(!keepExisting){
23823             this.clearSelections();
23824         }
23825             var ds = this.grid.store;
23826         for(var i = 0, len = records.length; i < len; i++){
23827             this.selectRow(ds.indexOf(records[i]), true);
23828         }
23829     },
23830
23831     /**
23832      * Gets the number of selected rows.
23833      * @return {Number}
23834      */
23835     getCount : function(){
23836         return this.selections.length;
23837     },
23838
23839     /**
23840      * Selects the first row in the grid.
23841      */
23842     selectFirstRow : function(){
23843         this.selectRow(0);
23844     },
23845
23846     /**
23847      * Select the last row.
23848      * @param {Boolean} keepExisting (optional) True to keep existing selections
23849      */
23850     selectLastRow : function(keepExisting){
23851         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23852         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23853     },
23854
23855     /**
23856      * Selects the row immediately following the last selected row.
23857      * @param {Boolean} keepExisting (optional) True to keep existing selections
23858      */
23859     selectNext : function(keepExisting)
23860     {
23861             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23862             this.selectRow(this.last+1, keepExisting);
23863             this.grid.getView().focusRow(this.last);
23864         }
23865     },
23866
23867     /**
23868      * Selects the row that precedes the last selected row.
23869      * @param {Boolean} keepExisting (optional) True to keep existing selections
23870      */
23871     selectPrevious : function(keepExisting){
23872         if(this.last){
23873             this.selectRow(this.last-1, keepExisting);
23874             this.grid.getView().focusRow(this.last);
23875         }
23876     },
23877
23878     /**
23879      * Returns the selected records
23880      * @return {Array} Array of selected records
23881      */
23882     getSelections : function(){
23883         return [].concat(this.selections.items);
23884     },
23885
23886     /**
23887      * Returns the first selected record.
23888      * @return {Record}
23889      */
23890     getSelected : function(){
23891         return this.selections.itemAt(0);
23892     },
23893
23894
23895     /**
23896      * Clears all selections.
23897      */
23898     clearSelections : function(fast)
23899     {
23900         if(this.locked) {
23901             return;
23902         }
23903         if(fast !== true){
23904                 var ds = this.grid.store;
23905             var s = this.selections;
23906             s.each(function(r){
23907                 this.deselectRow(ds.indexOfId(r.id));
23908             }, this);
23909             s.clear();
23910         }else{
23911             this.selections.clear();
23912         }
23913         this.last = false;
23914     },
23915
23916
23917     /**
23918      * Selects all rows.
23919      */
23920     selectAll : function(){
23921         if(this.locked) {
23922             return;
23923         }
23924         this.selections.clear();
23925         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23926             this.selectRow(i, true);
23927         }
23928     },
23929
23930     /**
23931      * Returns True if there is a selection.
23932      * @return {Boolean}
23933      */
23934     hasSelection : function(){
23935         return this.selections.length > 0;
23936     },
23937
23938     /**
23939      * Returns True if the specified row is selected.
23940      * @param {Number/Record} record The record or index of the record to check
23941      * @return {Boolean}
23942      */
23943     isSelected : function(index){
23944             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23945         return (r && this.selections.key(r.id) ? true : false);
23946     },
23947
23948     /**
23949      * Returns True if the specified record id is selected.
23950      * @param {String} id The id of record to check
23951      * @return {Boolean}
23952      */
23953     isIdSelected : function(id){
23954         return (this.selections.key(id) ? true : false);
23955     },
23956
23957
23958     // private
23959     handleMouseDBClick : function(e, t){
23960         
23961     },
23962     // private
23963     handleMouseDown : function(e, t)
23964     {
23965             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23966         if(this.isLocked() || rowIndex < 0 ){
23967             return;
23968         };
23969         if(e.shiftKey && this.last !== false){
23970             var last = this.last;
23971             this.selectRange(last, rowIndex, e.ctrlKey);
23972             this.last = last; // reset the last
23973             t.focus();
23974     
23975         }else{
23976             var isSelected = this.isSelected(rowIndex);
23977             //Roo.log("select row:" + rowIndex);
23978             if(isSelected){
23979                 this.deselectRow(rowIndex);
23980             } else {
23981                         this.selectRow(rowIndex, true);
23982             }
23983     
23984             /*
23985                 if(e.button !== 0 && isSelected){
23986                 alert('rowIndex 2: ' + rowIndex);
23987                     view.focusRow(rowIndex);
23988                 }else if(e.ctrlKey && isSelected){
23989                     this.deselectRow(rowIndex);
23990                 }else if(!isSelected){
23991                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23992                     view.focusRow(rowIndex);
23993                 }
23994             */
23995         }
23996         this.fireEvent("afterselectionchange", this);
23997     },
23998     // private
23999     handleDragableRowClick :  function(grid, rowIndex, e) 
24000     {
24001         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24002             this.selectRow(rowIndex, false);
24003             grid.view.focusRow(rowIndex);
24004              this.fireEvent("afterselectionchange", this);
24005         }
24006     },
24007     
24008     /**
24009      * Selects multiple rows.
24010      * @param {Array} rows Array of the indexes of the row to select
24011      * @param {Boolean} keepExisting (optional) True to keep existing selections
24012      */
24013     selectRows : function(rows, keepExisting){
24014         if(!keepExisting){
24015             this.clearSelections();
24016         }
24017         for(var i = 0, len = rows.length; i < len; i++){
24018             this.selectRow(rows[i], true);
24019         }
24020     },
24021
24022     /**
24023      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24024      * @param {Number} startRow The index of the first row in the range
24025      * @param {Number} endRow The index of the last row in the range
24026      * @param {Boolean} keepExisting (optional) True to retain existing selections
24027      */
24028     selectRange : function(startRow, endRow, keepExisting){
24029         if(this.locked) {
24030             return;
24031         }
24032         if(!keepExisting){
24033             this.clearSelections();
24034         }
24035         if(startRow <= endRow){
24036             for(var i = startRow; i <= endRow; i++){
24037                 this.selectRow(i, true);
24038             }
24039         }else{
24040             for(var i = startRow; i >= endRow; i--){
24041                 this.selectRow(i, true);
24042             }
24043         }
24044     },
24045
24046     /**
24047      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24048      * @param {Number} startRow The index of the first row in the range
24049      * @param {Number} endRow The index of the last row in the range
24050      */
24051     deselectRange : function(startRow, endRow, preventViewNotify){
24052         if(this.locked) {
24053             return;
24054         }
24055         for(var i = startRow; i <= endRow; i++){
24056             this.deselectRow(i, preventViewNotify);
24057         }
24058     },
24059
24060     /**
24061      * Selects a row.
24062      * @param {Number} row The index of the row to select
24063      * @param {Boolean} keepExisting (optional) True to keep existing selections
24064      */
24065     selectRow : function(index, keepExisting, preventViewNotify)
24066     {
24067             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24068             return;
24069         }
24070         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24071             if(!keepExisting || this.singleSelect){
24072                 this.clearSelections();
24073             }
24074             
24075             var r = this.grid.store.getAt(index);
24076             //console.log('selectRow - record id :' + r.id);
24077             
24078             this.selections.add(r);
24079             this.last = this.lastActive = index;
24080             if(!preventViewNotify){
24081                 var proxy = new Roo.Element(
24082                                 this.grid.getRowDom(index)
24083                 );
24084                 proxy.addClass('bg-info info');
24085             }
24086             this.fireEvent("rowselect", this, index, r);
24087             this.fireEvent("selectionchange", this);
24088         }
24089     },
24090
24091     /**
24092      * Deselects a row.
24093      * @param {Number} row The index of the row to deselect
24094      */
24095     deselectRow : function(index, preventViewNotify)
24096     {
24097         if(this.locked) {
24098             return;
24099         }
24100         if(this.last == index){
24101             this.last = false;
24102         }
24103         if(this.lastActive == index){
24104             this.lastActive = false;
24105         }
24106         
24107         var r = this.grid.store.getAt(index);
24108         if (!r) {
24109             return;
24110         }
24111         
24112         this.selections.remove(r);
24113         //.console.log('deselectRow - record id :' + r.id);
24114         if(!preventViewNotify){
24115         
24116             var proxy = new Roo.Element(
24117                 this.grid.getRowDom(index)
24118             );
24119             proxy.removeClass('bg-info info');
24120         }
24121         this.fireEvent("rowdeselect", this, index);
24122         this.fireEvent("selectionchange", this);
24123     },
24124
24125     // private
24126     restoreLast : function(){
24127         if(this._last){
24128             this.last = this._last;
24129         }
24130     },
24131
24132     // private
24133     acceptsNav : function(row, col, cm){
24134         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24135     },
24136
24137     // private
24138     onEditorKey : function(field, e){
24139         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24140         if(k == e.TAB){
24141             e.stopEvent();
24142             ed.completeEdit();
24143             if(e.shiftKey){
24144                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24145             }else{
24146                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24147             }
24148         }else if(k == e.ENTER && !e.ctrlKey){
24149             e.stopEvent();
24150             ed.completeEdit();
24151             if(e.shiftKey){
24152                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24153             }else{
24154                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24155             }
24156         }else if(k == e.ESC){
24157             ed.cancelEdit();
24158         }
24159         if(newCell){
24160             g.startEditing(newCell[0], newCell[1]);
24161         }
24162     }
24163 });
24164 /*
24165  * Based on:
24166  * Ext JS Library 1.1.1
24167  * Copyright(c) 2006-2007, Ext JS, LLC.
24168  *
24169  * Originally Released Under LGPL - original licence link has changed is not relivant.
24170  *
24171  * Fork - LGPL
24172  * <script type="text/javascript">
24173  */
24174  
24175 /**
24176  * @class Roo.bootstrap.PagingToolbar
24177  * @extends Roo.bootstrap.NavSimplebar
24178  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24179  * @constructor
24180  * Create a new PagingToolbar
24181  * @param {Object} config The config object
24182  * @param {Roo.data.Store} store
24183  */
24184 Roo.bootstrap.PagingToolbar = function(config)
24185 {
24186     // old args format still supported... - xtype is prefered..
24187         // created from xtype...
24188     
24189     this.ds = config.dataSource;
24190     
24191     if (config.store && !this.ds) {
24192         this.store= Roo.factory(config.store, Roo.data);
24193         this.ds = this.store;
24194         this.ds.xmodule = this.xmodule || false;
24195     }
24196     
24197     this.toolbarItems = [];
24198     if (config.items) {
24199         this.toolbarItems = config.items;
24200     }
24201     
24202     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24203     
24204     this.cursor = 0;
24205     
24206     if (this.ds) { 
24207         this.bind(this.ds);
24208     }
24209     
24210     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24211     
24212 };
24213
24214 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24215     /**
24216      * @cfg {Roo.data.Store} dataSource
24217      * The underlying data store providing the paged data
24218      */
24219     /**
24220      * @cfg {String/HTMLElement/Element} container
24221      * container The id or element that will contain the toolbar
24222      */
24223     /**
24224      * @cfg {Boolean} displayInfo
24225      * True to display the displayMsg (defaults to false)
24226      */
24227     /**
24228      * @cfg {Number} pageSize
24229      * The number of records to display per page (defaults to 20)
24230      */
24231     pageSize: 20,
24232     /**
24233      * @cfg {String} displayMsg
24234      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24235      */
24236     displayMsg : 'Displaying {0} - {1} of {2}',
24237     /**
24238      * @cfg {String} emptyMsg
24239      * The message to display when no records are found (defaults to "No data to display")
24240      */
24241     emptyMsg : 'No data to display',
24242     /**
24243      * Customizable piece of the default paging text (defaults to "Page")
24244      * @type String
24245      */
24246     beforePageText : "Page",
24247     /**
24248      * Customizable piece of the default paging text (defaults to "of %0")
24249      * @type String
24250      */
24251     afterPageText : "of {0}",
24252     /**
24253      * Customizable piece of the default paging text (defaults to "First Page")
24254      * @type String
24255      */
24256     firstText : "First Page",
24257     /**
24258      * Customizable piece of the default paging text (defaults to "Previous Page")
24259      * @type String
24260      */
24261     prevText : "Previous Page",
24262     /**
24263      * Customizable piece of the default paging text (defaults to "Next Page")
24264      * @type String
24265      */
24266     nextText : "Next Page",
24267     /**
24268      * Customizable piece of the default paging text (defaults to "Last Page")
24269      * @type String
24270      */
24271     lastText : "Last Page",
24272     /**
24273      * Customizable piece of the default paging text (defaults to "Refresh")
24274      * @type String
24275      */
24276     refreshText : "Refresh",
24277
24278     buttons : false,
24279     // private
24280     onRender : function(ct, position) 
24281     {
24282         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24283         this.navgroup.parentId = this.id;
24284         this.navgroup.onRender(this.el, null);
24285         // add the buttons to the navgroup
24286         
24287         if(this.displayInfo){
24288             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24289             this.displayEl = this.el.select('.x-paging-info', true).first();
24290 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24291 //            this.displayEl = navel.el.select('span',true).first();
24292         }
24293         
24294         var _this = this;
24295         
24296         if(this.buttons){
24297             Roo.each(_this.buttons, function(e){ // this might need to use render????
24298                Roo.factory(e).onRender(_this.el, null);
24299             });
24300         }
24301             
24302         Roo.each(_this.toolbarItems, function(e) {
24303             _this.navgroup.addItem(e);
24304         });
24305         
24306         
24307         this.first = this.navgroup.addItem({
24308             tooltip: this.firstText,
24309             cls: "prev",
24310             icon : 'fa fa-backward',
24311             disabled: true,
24312             preventDefault: true,
24313             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24314         });
24315         
24316         this.prev =  this.navgroup.addItem({
24317             tooltip: this.prevText,
24318             cls: "prev",
24319             icon : 'fa fa-step-backward',
24320             disabled: true,
24321             preventDefault: true,
24322             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24323         });
24324     //this.addSeparator();
24325         
24326         
24327         var field = this.navgroup.addItem( {
24328             tagtype : 'span',
24329             cls : 'x-paging-position',
24330             
24331             html : this.beforePageText  +
24332                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24333                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24334          } ); //?? escaped?
24335         
24336         this.field = field.el.select('input', true).first();
24337         this.field.on("keydown", this.onPagingKeydown, this);
24338         this.field.on("focus", function(){this.dom.select();});
24339     
24340     
24341         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24342         //this.field.setHeight(18);
24343         //this.addSeparator();
24344         this.next = this.navgroup.addItem({
24345             tooltip: this.nextText,
24346             cls: "next",
24347             html : ' <i class="fa fa-step-forward">',
24348             disabled: true,
24349             preventDefault: true,
24350             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24351         });
24352         this.last = this.navgroup.addItem({
24353             tooltip: this.lastText,
24354             icon : 'fa fa-forward',
24355             cls: "next",
24356             disabled: true,
24357             preventDefault: true,
24358             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24359         });
24360     //this.addSeparator();
24361         this.loading = this.navgroup.addItem({
24362             tooltip: this.refreshText,
24363             icon: 'fa fa-refresh',
24364             preventDefault: true,
24365             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24366         });
24367         
24368     },
24369
24370     // private
24371     updateInfo : function(){
24372         if(this.displayEl){
24373             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24374             var msg = count == 0 ?
24375                 this.emptyMsg :
24376                 String.format(
24377                     this.displayMsg,
24378                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24379                 );
24380             this.displayEl.update(msg);
24381         }
24382     },
24383
24384     // private
24385     onLoad : function(ds, r, o)
24386     {
24387         this.cursor = o.params ? o.params.start : 0;
24388         var d = this.getPageData(),
24389             ap = d.activePage,
24390             ps = d.pages;
24391         
24392         
24393         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24394         this.field.dom.value = ap;
24395         this.first.setDisabled(ap == 1);
24396         this.prev.setDisabled(ap == 1);
24397         this.next.setDisabled(ap == ps);
24398         this.last.setDisabled(ap == ps);
24399         this.loading.enable();
24400         this.updateInfo();
24401     },
24402
24403     // private
24404     getPageData : function(){
24405         var total = this.ds.getTotalCount();
24406         return {
24407             total : total,
24408             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24409             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24410         };
24411     },
24412
24413     // private
24414     onLoadError : function(){
24415         this.loading.enable();
24416     },
24417
24418     // private
24419     onPagingKeydown : function(e){
24420         var k = e.getKey();
24421         var d = this.getPageData();
24422         if(k == e.RETURN){
24423             var v = this.field.dom.value, pageNum;
24424             if(!v || isNaN(pageNum = parseInt(v, 10))){
24425                 this.field.dom.value = d.activePage;
24426                 return;
24427             }
24428             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24429             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24430             e.stopEvent();
24431         }
24432         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))
24433         {
24434           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24435           this.field.dom.value = pageNum;
24436           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24437           e.stopEvent();
24438         }
24439         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24440         {
24441           var v = this.field.dom.value, pageNum; 
24442           var increment = (e.shiftKey) ? 10 : 1;
24443           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24444                 increment *= -1;
24445           }
24446           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24447             this.field.dom.value = d.activePage;
24448             return;
24449           }
24450           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24451           {
24452             this.field.dom.value = parseInt(v, 10) + increment;
24453             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24454             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24455           }
24456           e.stopEvent();
24457         }
24458     },
24459
24460     // private
24461     beforeLoad : function(){
24462         if(this.loading){
24463             this.loading.disable();
24464         }
24465     },
24466
24467     // private
24468     onClick : function(which){
24469         
24470         var ds = this.ds;
24471         if (!ds) {
24472             return;
24473         }
24474         
24475         switch(which){
24476             case "first":
24477                 ds.load({params:{start: 0, limit: this.pageSize}});
24478             break;
24479             case "prev":
24480                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24481             break;
24482             case "next":
24483                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24484             break;
24485             case "last":
24486                 var total = ds.getTotalCount();
24487                 var extra = total % this.pageSize;
24488                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24489                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24490             break;
24491             case "refresh":
24492                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24493             break;
24494         }
24495     },
24496
24497     /**
24498      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24499      * @param {Roo.data.Store} store The data store to unbind
24500      */
24501     unbind : function(ds){
24502         ds.un("beforeload", this.beforeLoad, this);
24503         ds.un("load", this.onLoad, this);
24504         ds.un("loadexception", this.onLoadError, this);
24505         ds.un("remove", this.updateInfo, this);
24506         ds.un("add", this.updateInfo, this);
24507         this.ds = undefined;
24508     },
24509
24510     /**
24511      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24512      * @param {Roo.data.Store} store The data store to bind
24513      */
24514     bind : function(ds){
24515         ds.on("beforeload", this.beforeLoad, this);
24516         ds.on("load", this.onLoad, this);
24517         ds.on("loadexception", this.onLoadError, this);
24518         ds.on("remove", this.updateInfo, this);
24519         ds.on("add", this.updateInfo, this);
24520         this.ds = ds;
24521     }
24522 });/*
24523  * - LGPL
24524  *
24525  * element
24526  * 
24527  */
24528
24529 /**
24530  * @class Roo.bootstrap.MessageBar
24531  * @extends Roo.bootstrap.Component
24532  * Bootstrap MessageBar class
24533  * @cfg {String} html contents of the MessageBar
24534  * @cfg {String} weight (info | success | warning | danger) default info
24535  * @cfg {String} beforeClass insert the bar before the given class
24536  * @cfg {Boolean} closable (true | false) default false
24537  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24538  * 
24539  * @constructor
24540  * Create a new Element
24541  * @param {Object} config The config object
24542  */
24543
24544 Roo.bootstrap.MessageBar = function(config){
24545     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24546 };
24547
24548 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24549     
24550     html: '',
24551     weight: 'info',
24552     closable: false,
24553     fixed: false,
24554     beforeClass: 'bootstrap-sticky-wrap',
24555     
24556     getAutoCreate : function(){
24557         
24558         var cfg = {
24559             tag: 'div',
24560             cls: 'alert alert-dismissable alert-' + this.weight,
24561             cn: [
24562                 {
24563                     tag: 'span',
24564                     cls: 'message',
24565                     html: this.html || ''
24566                 }
24567             ]
24568         };
24569         
24570         if(this.fixed){
24571             cfg.cls += ' alert-messages-fixed';
24572         }
24573         
24574         if(this.closable){
24575             cfg.cn.push({
24576                 tag: 'button',
24577                 cls: 'close',
24578                 html: 'x'
24579             });
24580         }
24581         
24582         return cfg;
24583     },
24584     
24585     onRender : function(ct, position)
24586     {
24587         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24588         
24589         if(!this.el){
24590             var cfg = Roo.apply({},  this.getAutoCreate());
24591             cfg.id = Roo.id();
24592             
24593             if (this.cls) {
24594                 cfg.cls += ' ' + this.cls;
24595             }
24596             if (this.style) {
24597                 cfg.style = this.style;
24598             }
24599             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24600             
24601             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24602         }
24603         
24604         this.el.select('>button.close').on('click', this.hide, this);
24605         
24606     },
24607     
24608     show : function()
24609     {
24610         if (!this.rendered) {
24611             this.render();
24612         }
24613         
24614         this.el.show();
24615         
24616         this.fireEvent('show', this);
24617         
24618     },
24619     
24620     hide : function()
24621     {
24622         if (!this.rendered) {
24623             this.render();
24624         }
24625         
24626         this.el.hide();
24627         
24628         this.fireEvent('hide', this);
24629     },
24630     
24631     update : function()
24632     {
24633 //        var e = this.el.dom.firstChild;
24634 //        
24635 //        if(this.closable){
24636 //            e = e.nextSibling;
24637 //        }
24638 //        
24639 //        e.data = this.html || '';
24640
24641         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24642     }
24643    
24644 });
24645
24646  
24647
24648      /*
24649  * - LGPL
24650  *
24651  * Graph
24652  * 
24653  */
24654
24655
24656 /**
24657  * @class Roo.bootstrap.Graph
24658  * @extends Roo.bootstrap.Component
24659  * Bootstrap Graph class
24660 > Prameters
24661  -sm {number} sm 4
24662  -md {number} md 5
24663  @cfg {String} graphtype  bar | vbar | pie
24664  @cfg {number} g_x coodinator | centre x (pie)
24665  @cfg {number} g_y coodinator | centre y (pie)
24666  @cfg {number} g_r radius (pie)
24667  @cfg {number} g_height height of the chart (respected by all elements in the set)
24668  @cfg {number} g_width width of the chart (respected by all elements in the set)
24669  @cfg {Object} title The title of the chart
24670     
24671  -{Array}  values
24672  -opts (object) options for the chart 
24673      o {
24674      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24675      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24676      o vgutter (number)
24677      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.
24678      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24679      o to
24680      o stretch (boolean)
24681      o }
24682  -opts (object) options for the pie
24683      o{
24684      o cut
24685      o startAngle (number)
24686      o endAngle (number)
24687      } 
24688  *
24689  * @constructor
24690  * Create a new Input
24691  * @param {Object} config The config object
24692  */
24693
24694 Roo.bootstrap.Graph = function(config){
24695     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24696     
24697     this.addEvents({
24698         // img events
24699         /**
24700          * @event click
24701          * The img click event for the img.
24702          * @param {Roo.EventObject} e
24703          */
24704         "click" : true
24705     });
24706 };
24707
24708 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24709     
24710     sm: 4,
24711     md: 5,
24712     graphtype: 'bar',
24713     g_height: 250,
24714     g_width: 400,
24715     g_x: 50,
24716     g_y: 50,
24717     g_r: 30,
24718     opts:{
24719         //g_colors: this.colors,
24720         g_type: 'soft',
24721         g_gutter: '20%'
24722
24723     },
24724     title : false,
24725
24726     getAutoCreate : function(){
24727         
24728         var cfg = {
24729             tag: 'div',
24730             html : null
24731         };
24732         
24733         
24734         return  cfg;
24735     },
24736
24737     onRender : function(ct,position){
24738         
24739         
24740         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24741         
24742         if (typeof(Raphael) == 'undefined') {
24743             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24744             return;
24745         }
24746         
24747         this.raphael = Raphael(this.el.dom);
24748         
24749                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24750                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24751                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24752                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24753                 /*
24754                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24755                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24756                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24757                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24758                 
24759                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24760                 r.barchart(330, 10, 300, 220, data1);
24761                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24762                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24763                 */
24764                 
24765                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24766                 // r.barchart(30, 30, 560, 250,  xdata, {
24767                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24768                 //     axis : "0 0 1 1",
24769                 //     axisxlabels :  xdata
24770                 //     //yvalues : cols,
24771                    
24772                 // });
24773 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24774 //        
24775 //        this.load(null,xdata,{
24776 //                axis : "0 0 1 1",
24777 //                axisxlabels :  xdata
24778 //                });
24779
24780     },
24781
24782     load : function(graphtype,xdata,opts)
24783     {
24784         this.raphael.clear();
24785         if(!graphtype) {
24786             graphtype = this.graphtype;
24787         }
24788         if(!opts){
24789             opts = this.opts;
24790         }
24791         var r = this.raphael,
24792             fin = function () {
24793                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24794             },
24795             fout = function () {
24796                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24797             },
24798             pfin = function() {
24799                 this.sector.stop();
24800                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24801
24802                 if (this.label) {
24803                     this.label[0].stop();
24804                     this.label[0].attr({ r: 7.5 });
24805                     this.label[1].attr({ "font-weight": 800 });
24806                 }
24807             },
24808             pfout = function() {
24809                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24810
24811                 if (this.label) {
24812                     this.label[0].animate({ r: 5 }, 500, "bounce");
24813                     this.label[1].attr({ "font-weight": 400 });
24814                 }
24815             };
24816
24817         switch(graphtype){
24818             case 'bar':
24819                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24820                 break;
24821             case 'hbar':
24822                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24823                 break;
24824             case 'pie':
24825 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24826 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24827 //            
24828                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24829                 
24830                 break;
24831
24832         }
24833         
24834         if(this.title){
24835             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24836         }
24837         
24838     },
24839     
24840     setTitle: function(o)
24841     {
24842         this.title = o;
24843     },
24844     
24845     initEvents: function() {
24846         
24847         if(!this.href){
24848             this.el.on('click', this.onClick, this);
24849         }
24850     },
24851     
24852     onClick : function(e)
24853     {
24854         Roo.log('img onclick');
24855         this.fireEvent('click', this, e);
24856     }
24857    
24858 });
24859
24860  
24861 /*
24862  * - LGPL
24863  *
24864  * numberBox
24865  * 
24866  */
24867 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24868
24869 /**
24870  * @class Roo.bootstrap.dash.NumberBox
24871  * @extends Roo.bootstrap.Component
24872  * Bootstrap NumberBox class
24873  * @cfg {String} headline Box headline
24874  * @cfg {String} content Box content
24875  * @cfg {String} icon Box icon
24876  * @cfg {String} footer Footer text
24877  * @cfg {String} fhref Footer href
24878  * 
24879  * @constructor
24880  * Create a new NumberBox
24881  * @param {Object} config The config object
24882  */
24883
24884
24885 Roo.bootstrap.dash.NumberBox = function(config){
24886     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24887     
24888 };
24889
24890 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24891     
24892     headline : '',
24893     content : '',
24894     icon : '',
24895     footer : '',
24896     fhref : '',
24897     ficon : '',
24898     
24899     getAutoCreate : function(){
24900         
24901         var cfg = {
24902             tag : 'div',
24903             cls : 'small-box ',
24904             cn : [
24905                 {
24906                     tag : 'div',
24907                     cls : 'inner',
24908                     cn :[
24909                         {
24910                             tag : 'h3',
24911                             cls : 'roo-headline',
24912                             html : this.headline
24913                         },
24914                         {
24915                             tag : 'p',
24916                             cls : 'roo-content',
24917                             html : this.content
24918                         }
24919                     ]
24920                 }
24921             ]
24922         };
24923         
24924         if(this.icon){
24925             cfg.cn.push({
24926                 tag : 'div',
24927                 cls : 'icon',
24928                 cn :[
24929                     {
24930                         tag : 'i',
24931                         cls : 'ion ' + this.icon
24932                     }
24933                 ]
24934             });
24935         }
24936         
24937         if(this.footer){
24938             var footer = {
24939                 tag : 'a',
24940                 cls : 'small-box-footer',
24941                 href : this.fhref || '#',
24942                 html : this.footer
24943             };
24944             
24945             cfg.cn.push(footer);
24946             
24947         }
24948         
24949         return  cfg;
24950     },
24951
24952     onRender : function(ct,position){
24953         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24954
24955
24956        
24957                 
24958     },
24959
24960     setHeadline: function (value)
24961     {
24962         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24963     },
24964     
24965     setFooter: function (value, href)
24966     {
24967         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24968         
24969         if(href){
24970             this.el.select('a.small-box-footer',true).first().attr('href', href);
24971         }
24972         
24973     },
24974
24975     setContent: function (value)
24976     {
24977         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24978     },
24979
24980     initEvents: function() 
24981     {   
24982         
24983     }
24984     
24985 });
24986
24987  
24988 /*
24989  * - LGPL
24990  *
24991  * TabBox
24992  * 
24993  */
24994 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24995
24996 /**
24997  * @class Roo.bootstrap.dash.TabBox
24998  * @extends Roo.bootstrap.Component
24999  * Bootstrap TabBox class
25000  * @cfg {String} title Title of the TabBox
25001  * @cfg {String} icon Icon of the TabBox
25002  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25003  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25004  * 
25005  * @constructor
25006  * Create a new TabBox
25007  * @param {Object} config The config object
25008  */
25009
25010
25011 Roo.bootstrap.dash.TabBox = function(config){
25012     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25013     this.addEvents({
25014         // raw events
25015         /**
25016          * @event addpane
25017          * When a pane is added
25018          * @param {Roo.bootstrap.dash.TabPane} pane
25019          */
25020         "addpane" : true,
25021         /**
25022          * @event activatepane
25023          * When a pane is activated
25024          * @param {Roo.bootstrap.dash.TabPane} pane
25025          */
25026         "activatepane" : true
25027         
25028          
25029     });
25030     
25031     this.panes = [];
25032 };
25033
25034 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25035
25036     title : '',
25037     icon : false,
25038     showtabs : true,
25039     tabScrollable : false,
25040     
25041     getChildContainer : function()
25042     {
25043         return this.el.select('.tab-content', true).first();
25044     },
25045     
25046     getAutoCreate : function(){
25047         
25048         var header = {
25049             tag: 'li',
25050             cls: 'pull-left header',
25051             html: this.title,
25052             cn : []
25053         };
25054         
25055         if(this.icon){
25056             header.cn.push({
25057                 tag: 'i',
25058                 cls: 'fa ' + this.icon
25059             });
25060         }
25061         
25062         var h = {
25063             tag: 'ul',
25064             cls: 'nav nav-tabs pull-right',
25065             cn: [
25066                 header
25067             ]
25068         };
25069         
25070         if(this.tabScrollable){
25071             h = {
25072                 tag: 'div',
25073                 cls: 'tab-header',
25074                 cn: [
25075                     {
25076                         tag: 'ul',
25077                         cls: 'nav nav-tabs pull-right',
25078                         cn: [
25079                             header
25080                         ]
25081                     }
25082                 ]
25083             };
25084         }
25085         
25086         var cfg = {
25087             tag: 'div',
25088             cls: 'nav-tabs-custom',
25089             cn: [
25090                 h,
25091                 {
25092                     tag: 'div',
25093                     cls: 'tab-content no-padding',
25094                     cn: []
25095                 }
25096             ]
25097         };
25098
25099         return  cfg;
25100     },
25101     initEvents : function()
25102     {
25103         //Roo.log('add add pane handler');
25104         this.on('addpane', this.onAddPane, this);
25105     },
25106      /**
25107      * Updates the box title
25108      * @param {String} html to set the title to.
25109      */
25110     setTitle : function(value)
25111     {
25112         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25113     },
25114     onAddPane : function(pane)
25115     {
25116         this.panes.push(pane);
25117         //Roo.log('addpane');
25118         //Roo.log(pane);
25119         // tabs are rendere left to right..
25120         if(!this.showtabs){
25121             return;
25122         }
25123         
25124         var ctr = this.el.select('.nav-tabs', true).first();
25125          
25126          
25127         var existing = ctr.select('.nav-tab',true);
25128         var qty = existing.getCount();;
25129         
25130         
25131         var tab = ctr.createChild({
25132             tag : 'li',
25133             cls : 'nav-tab' + (qty ? '' : ' active'),
25134             cn : [
25135                 {
25136                     tag : 'a',
25137                     href:'#',
25138                     html : pane.title
25139                 }
25140             ]
25141         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25142         pane.tab = tab;
25143         
25144         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25145         if (!qty) {
25146             pane.el.addClass('active');
25147         }
25148         
25149                 
25150     },
25151     onTabClick : function(ev,un,ob,pane)
25152     {
25153         //Roo.log('tab - prev default');
25154         ev.preventDefault();
25155         
25156         
25157         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25158         pane.tab.addClass('active');
25159         //Roo.log(pane.title);
25160         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25161         // technically we should have a deactivate event.. but maybe add later.
25162         // and it should not de-activate the selected tab...
25163         this.fireEvent('activatepane', pane);
25164         pane.el.addClass('active');
25165         pane.fireEvent('activate');
25166         
25167         
25168     },
25169     
25170     getActivePane : function()
25171     {
25172         var r = false;
25173         Roo.each(this.panes, function(p) {
25174             if(p.el.hasClass('active')){
25175                 r = p;
25176                 return false;
25177             }
25178             
25179             return;
25180         });
25181         
25182         return r;
25183     }
25184     
25185     
25186 });
25187
25188  
25189 /*
25190  * - LGPL
25191  *
25192  * Tab pane
25193  * 
25194  */
25195 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25196 /**
25197  * @class Roo.bootstrap.TabPane
25198  * @extends Roo.bootstrap.Component
25199  * Bootstrap TabPane class
25200  * @cfg {Boolean} active (false | true) Default false
25201  * @cfg {String} title title of panel
25202
25203  * 
25204  * @constructor
25205  * Create a new TabPane
25206  * @param {Object} config The config object
25207  */
25208
25209 Roo.bootstrap.dash.TabPane = function(config){
25210     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25211     
25212     this.addEvents({
25213         // raw events
25214         /**
25215          * @event activate
25216          * When a pane is activated
25217          * @param {Roo.bootstrap.dash.TabPane} pane
25218          */
25219         "activate" : true
25220          
25221     });
25222 };
25223
25224 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25225     
25226     active : false,
25227     title : '',
25228     
25229     // the tabBox that this is attached to.
25230     tab : false,
25231      
25232     getAutoCreate : function() 
25233     {
25234         var cfg = {
25235             tag: 'div',
25236             cls: 'tab-pane'
25237         };
25238         
25239         if(this.active){
25240             cfg.cls += ' active';
25241         }
25242         
25243         return cfg;
25244     },
25245     initEvents  : function()
25246     {
25247         //Roo.log('trigger add pane handler');
25248         this.parent().fireEvent('addpane', this)
25249     },
25250     
25251      /**
25252      * Updates the tab title 
25253      * @param {String} html to set the title to.
25254      */
25255     setTitle: function(str)
25256     {
25257         if (!this.tab) {
25258             return;
25259         }
25260         this.title = str;
25261         this.tab.select('a', true).first().dom.innerHTML = str;
25262         
25263     }
25264     
25265     
25266     
25267 });
25268
25269  
25270
25271
25272  /*
25273  * - LGPL
25274  *
25275  * menu
25276  * 
25277  */
25278 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25279
25280 /**
25281  * @class Roo.bootstrap.menu.Menu
25282  * @extends Roo.bootstrap.Component
25283  * Bootstrap Menu class - container for Menu
25284  * @cfg {String} html Text of the menu
25285  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25286  * @cfg {String} icon Font awesome icon
25287  * @cfg {String} pos Menu align to (top | bottom) default bottom
25288  * 
25289  * 
25290  * @constructor
25291  * Create a new Menu
25292  * @param {Object} config The config object
25293  */
25294
25295
25296 Roo.bootstrap.menu.Menu = function(config){
25297     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25298     
25299     this.addEvents({
25300         /**
25301          * @event beforeshow
25302          * Fires before this menu is displayed
25303          * @param {Roo.bootstrap.menu.Menu} this
25304          */
25305         beforeshow : true,
25306         /**
25307          * @event beforehide
25308          * Fires before this menu is hidden
25309          * @param {Roo.bootstrap.menu.Menu} this
25310          */
25311         beforehide : true,
25312         /**
25313          * @event show
25314          * Fires after this menu is displayed
25315          * @param {Roo.bootstrap.menu.Menu} this
25316          */
25317         show : true,
25318         /**
25319          * @event hide
25320          * Fires after this menu is hidden
25321          * @param {Roo.bootstrap.menu.Menu} this
25322          */
25323         hide : true,
25324         /**
25325          * @event click
25326          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25327          * @param {Roo.bootstrap.menu.Menu} this
25328          * @param {Roo.EventObject} e
25329          */
25330         click : true
25331     });
25332     
25333 };
25334
25335 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25336     
25337     submenu : false,
25338     html : '',
25339     weight : 'default',
25340     icon : false,
25341     pos : 'bottom',
25342     
25343     
25344     getChildContainer : function() {
25345         if(this.isSubMenu){
25346             return this.el;
25347         }
25348         
25349         return this.el.select('ul.dropdown-menu', true).first();  
25350     },
25351     
25352     getAutoCreate : function()
25353     {
25354         var text = [
25355             {
25356                 tag : 'span',
25357                 cls : 'roo-menu-text',
25358                 html : this.html
25359             }
25360         ];
25361         
25362         if(this.icon){
25363             text.unshift({
25364                 tag : 'i',
25365                 cls : 'fa ' + this.icon
25366             })
25367         }
25368         
25369         
25370         var cfg = {
25371             tag : 'div',
25372             cls : 'btn-group',
25373             cn : [
25374                 {
25375                     tag : 'button',
25376                     cls : 'dropdown-button btn btn-' + this.weight,
25377                     cn : text
25378                 },
25379                 {
25380                     tag : 'button',
25381                     cls : 'dropdown-toggle btn btn-' + this.weight,
25382                     cn : [
25383                         {
25384                             tag : 'span',
25385                             cls : 'caret'
25386                         }
25387                     ]
25388                 },
25389                 {
25390                     tag : 'ul',
25391                     cls : 'dropdown-menu'
25392                 }
25393             ]
25394             
25395         };
25396         
25397         if(this.pos == 'top'){
25398             cfg.cls += ' dropup';
25399         }
25400         
25401         if(this.isSubMenu){
25402             cfg = {
25403                 tag : 'ul',
25404                 cls : 'dropdown-menu'
25405             }
25406         }
25407         
25408         return cfg;
25409     },
25410     
25411     onRender : function(ct, position)
25412     {
25413         this.isSubMenu = ct.hasClass('dropdown-submenu');
25414         
25415         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25416     },
25417     
25418     initEvents : function() 
25419     {
25420         if(this.isSubMenu){
25421             return;
25422         }
25423         
25424         this.hidden = true;
25425         
25426         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25427         this.triggerEl.on('click', this.onTriggerPress, this);
25428         
25429         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25430         this.buttonEl.on('click', this.onClick, this);
25431         
25432     },
25433     
25434     list : function()
25435     {
25436         if(this.isSubMenu){
25437             return this.el;
25438         }
25439         
25440         return this.el.select('ul.dropdown-menu', true).first();
25441     },
25442     
25443     onClick : function(e)
25444     {
25445         this.fireEvent("click", this, e);
25446     },
25447     
25448     onTriggerPress  : function(e)
25449     {   
25450         if (this.isVisible()) {
25451             this.hide();
25452         } else {
25453             this.show();
25454         }
25455     },
25456     
25457     isVisible : function(){
25458         return !this.hidden;
25459     },
25460     
25461     show : function()
25462     {
25463         this.fireEvent("beforeshow", this);
25464         
25465         this.hidden = false;
25466         this.el.addClass('open');
25467         
25468         Roo.get(document).on("mouseup", this.onMouseUp, this);
25469         
25470         this.fireEvent("show", this);
25471         
25472         
25473     },
25474     
25475     hide : function()
25476     {
25477         this.fireEvent("beforehide", this);
25478         
25479         this.hidden = true;
25480         this.el.removeClass('open');
25481         
25482         Roo.get(document).un("mouseup", this.onMouseUp);
25483         
25484         this.fireEvent("hide", this);
25485     },
25486     
25487     onMouseUp : function()
25488     {
25489         this.hide();
25490     }
25491     
25492 });
25493
25494  
25495  /*
25496  * - LGPL
25497  *
25498  * menu item
25499  * 
25500  */
25501 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25502
25503 /**
25504  * @class Roo.bootstrap.menu.Item
25505  * @extends Roo.bootstrap.Component
25506  * Bootstrap MenuItem class
25507  * @cfg {Boolean} submenu (true | false) default false
25508  * @cfg {String} html text of the item
25509  * @cfg {String} href the link
25510  * @cfg {Boolean} disable (true | false) default false
25511  * @cfg {Boolean} preventDefault (true | false) default true
25512  * @cfg {String} icon Font awesome icon
25513  * @cfg {String} pos Submenu align to (left | right) default right 
25514  * 
25515  * 
25516  * @constructor
25517  * Create a new Item
25518  * @param {Object} config The config object
25519  */
25520
25521
25522 Roo.bootstrap.menu.Item = function(config){
25523     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25524     this.addEvents({
25525         /**
25526          * @event mouseover
25527          * Fires when the mouse is hovering over this menu
25528          * @param {Roo.bootstrap.menu.Item} this
25529          * @param {Roo.EventObject} e
25530          */
25531         mouseover : true,
25532         /**
25533          * @event mouseout
25534          * Fires when the mouse exits this menu
25535          * @param {Roo.bootstrap.menu.Item} this
25536          * @param {Roo.EventObject} e
25537          */
25538         mouseout : true,
25539         // raw events
25540         /**
25541          * @event click
25542          * The raw click event for the entire grid.
25543          * @param {Roo.EventObject} e
25544          */
25545         click : true
25546     });
25547 };
25548
25549 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25550     
25551     submenu : false,
25552     href : '',
25553     html : '',
25554     preventDefault: true,
25555     disable : false,
25556     icon : false,
25557     pos : 'right',
25558     
25559     getAutoCreate : function()
25560     {
25561         var text = [
25562             {
25563                 tag : 'span',
25564                 cls : 'roo-menu-item-text',
25565                 html : this.html
25566             }
25567         ];
25568         
25569         if(this.icon){
25570             text.unshift({
25571                 tag : 'i',
25572                 cls : 'fa ' + this.icon
25573             })
25574         }
25575         
25576         var cfg = {
25577             tag : 'li',
25578             cn : [
25579                 {
25580                     tag : 'a',
25581                     href : this.href || '#',
25582                     cn : text
25583                 }
25584             ]
25585         };
25586         
25587         if(this.disable){
25588             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25589         }
25590         
25591         if(this.submenu){
25592             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25593             
25594             if(this.pos == 'left'){
25595                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25596             }
25597         }
25598         
25599         return cfg;
25600     },
25601     
25602     initEvents : function() 
25603     {
25604         this.el.on('mouseover', this.onMouseOver, this);
25605         this.el.on('mouseout', this.onMouseOut, this);
25606         
25607         this.el.select('a', true).first().on('click', this.onClick, this);
25608         
25609     },
25610     
25611     onClick : function(e)
25612     {
25613         if(this.preventDefault){
25614             e.preventDefault();
25615         }
25616         
25617         this.fireEvent("click", this, e);
25618     },
25619     
25620     onMouseOver : function(e)
25621     {
25622         if(this.submenu && this.pos == 'left'){
25623             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25624         }
25625         
25626         this.fireEvent("mouseover", this, e);
25627     },
25628     
25629     onMouseOut : function(e)
25630     {
25631         this.fireEvent("mouseout", this, e);
25632     }
25633 });
25634
25635  
25636
25637  /*
25638  * - LGPL
25639  *
25640  * menu separator
25641  * 
25642  */
25643 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25644
25645 /**
25646  * @class Roo.bootstrap.menu.Separator
25647  * @extends Roo.bootstrap.Component
25648  * Bootstrap Separator class
25649  * 
25650  * @constructor
25651  * Create a new Separator
25652  * @param {Object} config The config object
25653  */
25654
25655
25656 Roo.bootstrap.menu.Separator = function(config){
25657     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25658 };
25659
25660 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25661     
25662     getAutoCreate : function(){
25663         var cfg = {
25664             tag : 'li',
25665             cls: 'divider'
25666         };
25667         
25668         return cfg;
25669     }
25670    
25671 });
25672
25673  
25674
25675  /*
25676  * - LGPL
25677  *
25678  * Tooltip
25679  * 
25680  */
25681
25682 /**
25683  * @class Roo.bootstrap.Tooltip
25684  * Bootstrap Tooltip class
25685  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25686  * to determine which dom element triggers the tooltip.
25687  * 
25688  * It needs to add support for additional attributes like tooltip-position
25689  * 
25690  * @constructor
25691  * Create a new Toolti
25692  * @param {Object} config The config object
25693  */
25694
25695 Roo.bootstrap.Tooltip = function(config){
25696     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25697     
25698     this.alignment = Roo.bootstrap.Tooltip.alignment;
25699     
25700     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25701         this.alignment = config.alignment;
25702     }
25703     
25704 };
25705
25706 Roo.apply(Roo.bootstrap.Tooltip, {
25707     /**
25708      * @function init initialize tooltip monitoring.
25709      * @static
25710      */
25711     currentEl : false,
25712     currentTip : false,
25713     currentRegion : false,
25714     
25715     //  init : delay?
25716     
25717     init : function()
25718     {
25719         Roo.get(document).on('mouseover', this.enter ,this);
25720         Roo.get(document).on('mouseout', this.leave, this);
25721          
25722         
25723         this.currentTip = new Roo.bootstrap.Tooltip();
25724     },
25725     
25726     enter : function(ev)
25727     {
25728         var dom = ev.getTarget();
25729         
25730         //Roo.log(['enter',dom]);
25731         var el = Roo.fly(dom);
25732         if (this.currentEl) {
25733             //Roo.log(dom);
25734             //Roo.log(this.currentEl);
25735             //Roo.log(this.currentEl.contains(dom));
25736             if (this.currentEl == el) {
25737                 return;
25738             }
25739             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25740                 return;
25741             }
25742
25743         }
25744         
25745         if (this.currentTip.el) {
25746             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25747         }    
25748         //Roo.log(ev);
25749         
25750         if(!el || el.dom == document){
25751             return;
25752         }
25753         
25754         var bindEl = el;
25755         
25756         // you can not look for children, as if el is the body.. then everythign is the child..
25757         if (!el.attr('tooltip')) { //
25758             if (!el.select("[tooltip]").elements.length) {
25759                 return;
25760             }
25761             // is the mouse over this child...?
25762             bindEl = el.select("[tooltip]").first();
25763             var xy = ev.getXY();
25764             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25765                 //Roo.log("not in region.");
25766                 return;
25767             }
25768             //Roo.log("child element over..");
25769             
25770         }
25771         this.currentEl = bindEl;
25772         this.currentTip.bind(bindEl);
25773         this.currentRegion = Roo.lib.Region.getRegion(dom);
25774         this.currentTip.enter();
25775         
25776     },
25777     leave : function(ev)
25778     {
25779         var dom = ev.getTarget();
25780         //Roo.log(['leave',dom]);
25781         if (!this.currentEl) {
25782             return;
25783         }
25784         
25785         
25786         if (dom != this.currentEl.dom) {
25787             return;
25788         }
25789         var xy = ev.getXY();
25790         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25791             return;
25792         }
25793         // only activate leave if mouse cursor is outside... bounding box..
25794         
25795         
25796         
25797         
25798         if (this.currentTip) {
25799             this.currentTip.leave();
25800         }
25801         //Roo.log('clear currentEl');
25802         this.currentEl = false;
25803         
25804         
25805     },
25806     alignment : {
25807         'left' : ['r-l', [-2,0], 'right'],
25808         'right' : ['l-r', [2,0], 'left'],
25809         'bottom' : ['t-b', [0,2], 'top'],
25810         'top' : [ 'b-t', [0,-2], 'bottom']
25811     }
25812     
25813 });
25814
25815
25816 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25817     
25818     
25819     bindEl : false,
25820     
25821     delay : null, // can be { show : 300 , hide: 500}
25822     
25823     timeout : null,
25824     
25825     hoverState : null, //???
25826     
25827     placement : 'bottom', 
25828     
25829     alignment : false,
25830     
25831     getAutoCreate : function(){
25832     
25833         var cfg = {
25834            cls : 'tooltip',
25835            role : 'tooltip',
25836            cn : [
25837                 {
25838                     cls : 'tooltip-arrow'
25839                 },
25840                 {
25841                     cls : 'tooltip-inner'
25842                 }
25843            ]
25844         };
25845         
25846         return cfg;
25847     },
25848     bind : function(el)
25849     {
25850         this.bindEl = el;
25851     },
25852       
25853     
25854     enter : function () {
25855        
25856         if (this.timeout != null) {
25857             clearTimeout(this.timeout);
25858         }
25859         
25860         this.hoverState = 'in';
25861          //Roo.log("enter - show");
25862         if (!this.delay || !this.delay.show) {
25863             this.show();
25864             return;
25865         }
25866         var _t = this;
25867         this.timeout = setTimeout(function () {
25868             if (_t.hoverState == 'in') {
25869                 _t.show();
25870             }
25871         }, this.delay.show);
25872     },
25873     leave : function()
25874     {
25875         clearTimeout(this.timeout);
25876     
25877         this.hoverState = 'out';
25878          if (!this.delay || !this.delay.hide) {
25879             this.hide();
25880             return;
25881         }
25882        
25883         var _t = this;
25884         this.timeout = setTimeout(function () {
25885             //Roo.log("leave - timeout");
25886             
25887             if (_t.hoverState == 'out') {
25888                 _t.hide();
25889                 Roo.bootstrap.Tooltip.currentEl = false;
25890             }
25891         }, delay);
25892     },
25893     
25894     show : function (msg)
25895     {
25896         if (!this.el) {
25897             this.render(document.body);
25898         }
25899         // set content.
25900         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25901         
25902         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25903         
25904         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25905         
25906         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25907         
25908         var placement = typeof this.placement == 'function' ?
25909             this.placement.call(this, this.el, on_el) :
25910             this.placement;
25911             
25912         var autoToken = /\s?auto?\s?/i;
25913         var autoPlace = autoToken.test(placement);
25914         if (autoPlace) {
25915             placement = placement.replace(autoToken, '') || 'top';
25916         }
25917         
25918         //this.el.detach()
25919         //this.el.setXY([0,0]);
25920         this.el.show();
25921         //this.el.dom.style.display='block';
25922         
25923         //this.el.appendTo(on_el);
25924         
25925         var p = this.getPosition();
25926         var box = this.el.getBox();
25927         
25928         if (autoPlace) {
25929             // fixme..
25930         }
25931         
25932         var align = this.alignment[placement];
25933         
25934         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25935         
25936         if(placement == 'top' || placement == 'bottom'){
25937             if(xy[0] < 0){
25938                 placement = 'right';
25939             }
25940             
25941             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25942                 placement = 'left';
25943             }
25944             
25945             var scroll = Roo.select('body', true).first().getScroll();
25946             
25947             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25948                 placement = 'top';
25949             }
25950             
25951         }
25952         
25953         this.el.alignTo(this.bindEl, align[0],align[1]);
25954         //var arrow = this.el.select('.arrow',true).first();
25955         //arrow.set(align[2], 
25956         
25957         this.el.addClass(placement);
25958         
25959         this.el.addClass('in fade');
25960         
25961         this.hoverState = null;
25962         
25963         if (this.el.hasClass('fade')) {
25964             // fade it?
25965         }
25966         
25967     },
25968     hide : function()
25969     {
25970          
25971         if (!this.el) {
25972             return;
25973         }
25974         //this.el.setXY([0,0]);
25975         this.el.removeClass('in');
25976         //this.el.hide();
25977         
25978     }
25979     
25980 });
25981  
25982
25983  /*
25984  * - LGPL
25985  *
25986  * Location Picker
25987  * 
25988  */
25989
25990 /**
25991  * @class Roo.bootstrap.LocationPicker
25992  * @extends Roo.bootstrap.Component
25993  * Bootstrap LocationPicker class
25994  * @cfg {Number} latitude Position when init default 0
25995  * @cfg {Number} longitude Position when init default 0
25996  * @cfg {Number} zoom default 15
25997  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25998  * @cfg {Boolean} mapTypeControl default false
25999  * @cfg {Boolean} disableDoubleClickZoom default false
26000  * @cfg {Boolean} scrollwheel default true
26001  * @cfg {Boolean} streetViewControl default false
26002  * @cfg {Number} radius default 0
26003  * @cfg {String} locationName
26004  * @cfg {Boolean} draggable default true
26005  * @cfg {Boolean} enableAutocomplete default false
26006  * @cfg {Boolean} enableReverseGeocode default true
26007  * @cfg {String} markerTitle
26008  * 
26009  * @constructor
26010  * Create a new LocationPicker
26011  * @param {Object} config The config object
26012  */
26013
26014
26015 Roo.bootstrap.LocationPicker = function(config){
26016     
26017     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26018     
26019     this.addEvents({
26020         /**
26021          * @event initial
26022          * Fires when the picker initialized.
26023          * @param {Roo.bootstrap.LocationPicker} this
26024          * @param {Google Location} location
26025          */
26026         initial : true,
26027         /**
26028          * @event positionchanged
26029          * Fires when the picker position changed.
26030          * @param {Roo.bootstrap.LocationPicker} this
26031          * @param {Google Location} location
26032          */
26033         positionchanged : true,
26034         /**
26035          * @event resize
26036          * Fires when the map resize.
26037          * @param {Roo.bootstrap.LocationPicker} this
26038          */
26039         resize : true,
26040         /**
26041          * @event show
26042          * Fires when the map show.
26043          * @param {Roo.bootstrap.LocationPicker} this
26044          */
26045         show : true,
26046         /**
26047          * @event hide
26048          * Fires when the map hide.
26049          * @param {Roo.bootstrap.LocationPicker} this
26050          */
26051         hide : true,
26052         /**
26053          * @event mapClick
26054          * Fires when click the map.
26055          * @param {Roo.bootstrap.LocationPicker} this
26056          * @param {Map event} e
26057          */
26058         mapClick : true,
26059         /**
26060          * @event mapRightClick
26061          * Fires when right click the map.
26062          * @param {Roo.bootstrap.LocationPicker} this
26063          * @param {Map event} e
26064          */
26065         mapRightClick : true,
26066         /**
26067          * @event markerClick
26068          * Fires when click the marker.
26069          * @param {Roo.bootstrap.LocationPicker} this
26070          * @param {Map event} e
26071          */
26072         markerClick : true,
26073         /**
26074          * @event markerRightClick
26075          * Fires when right click the marker.
26076          * @param {Roo.bootstrap.LocationPicker} this
26077          * @param {Map event} e
26078          */
26079         markerRightClick : true,
26080         /**
26081          * @event OverlayViewDraw
26082          * Fires when OverlayView Draw
26083          * @param {Roo.bootstrap.LocationPicker} this
26084          */
26085         OverlayViewDraw : true,
26086         /**
26087          * @event OverlayViewOnAdd
26088          * Fires when OverlayView Draw
26089          * @param {Roo.bootstrap.LocationPicker} this
26090          */
26091         OverlayViewOnAdd : true,
26092         /**
26093          * @event OverlayViewOnRemove
26094          * Fires when OverlayView Draw
26095          * @param {Roo.bootstrap.LocationPicker} this
26096          */
26097         OverlayViewOnRemove : true,
26098         /**
26099          * @event OverlayViewShow
26100          * Fires when OverlayView Draw
26101          * @param {Roo.bootstrap.LocationPicker} this
26102          * @param {Pixel} cpx
26103          */
26104         OverlayViewShow : true,
26105         /**
26106          * @event OverlayViewHide
26107          * Fires when OverlayView Draw
26108          * @param {Roo.bootstrap.LocationPicker} this
26109          */
26110         OverlayViewHide : true,
26111         /**
26112          * @event loadexception
26113          * Fires when load google lib failed.
26114          * @param {Roo.bootstrap.LocationPicker} this
26115          */
26116         loadexception : true
26117     });
26118         
26119 };
26120
26121 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26122     
26123     gMapContext: false,
26124     
26125     latitude: 0,
26126     longitude: 0,
26127     zoom: 15,
26128     mapTypeId: false,
26129     mapTypeControl: false,
26130     disableDoubleClickZoom: false,
26131     scrollwheel: true,
26132     streetViewControl: false,
26133     radius: 0,
26134     locationName: '',
26135     draggable: true,
26136     enableAutocomplete: false,
26137     enableReverseGeocode: true,
26138     markerTitle: '',
26139     
26140     getAutoCreate: function()
26141     {
26142
26143         var cfg = {
26144             tag: 'div',
26145             cls: 'roo-location-picker'
26146         };
26147         
26148         return cfg
26149     },
26150     
26151     initEvents: function(ct, position)
26152     {       
26153         if(!this.el.getWidth() || this.isApplied()){
26154             return;
26155         }
26156         
26157         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26158         
26159         this.initial();
26160     },
26161     
26162     initial: function()
26163     {
26164         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26165             this.fireEvent('loadexception', this);
26166             return;
26167         }
26168         
26169         if(!this.mapTypeId){
26170             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26171         }
26172         
26173         this.gMapContext = this.GMapContext();
26174         
26175         this.initOverlayView();
26176         
26177         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26178         
26179         var _this = this;
26180                 
26181         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26182             _this.setPosition(_this.gMapContext.marker.position);
26183         });
26184         
26185         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26186             _this.fireEvent('mapClick', this, event);
26187             
26188         });
26189
26190         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26191             _this.fireEvent('mapRightClick', this, event);
26192             
26193         });
26194         
26195         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26196             _this.fireEvent('markerClick', this, event);
26197             
26198         });
26199
26200         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26201             _this.fireEvent('markerRightClick', this, event);
26202             
26203         });
26204         
26205         this.setPosition(this.gMapContext.location);
26206         
26207         this.fireEvent('initial', this, this.gMapContext.location);
26208     },
26209     
26210     initOverlayView: function()
26211     {
26212         var _this = this;
26213         
26214         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26215             
26216             draw: function()
26217             {
26218                 _this.fireEvent('OverlayViewDraw', _this);
26219             },
26220             
26221             onAdd: function()
26222             {
26223                 _this.fireEvent('OverlayViewOnAdd', _this);
26224             },
26225             
26226             onRemove: function()
26227             {
26228                 _this.fireEvent('OverlayViewOnRemove', _this);
26229             },
26230             
26231             show: function(cpx)
26232             {
26233                 _this.fireEvent('OverlayViewShow', _this, cpx);
26234             },
26235             
26236             hide: function()
26237             {
26238                 _this.fireEvent('OverlayViewHide', _this);
26239             }
26240             
26241         });
26242     },
26243     
26244     fromLatLngToContainerPixel: function(event)
26245     {
26246         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26247     },
26248     
26249     isApplied: function() 
26250     {
26251         return this.getGmapContext() == false ? false : true;
26252     },
26253     
26254     getGmapContext: function() 
26255     {
26256         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26257     },
26258     
26259     GMapContext: function() 
26260     {
26261         var position = new google.maps.LatLng(this.latitude, this.longitude);
26262         
26263         var _map = new google.maps.Map(this.el.dom, {
26264             center: position,
26265             zoom: this.zoom,
26266             mapTypeId: this.mapTypeId,
26267             mapTypeControl: this.mapTypeControl,
26268             disableDoubleClickZoom: this.disableDoubleClickZoom,
26269             scrollwheel: this.scrollwheel,
26270             streetViewControl: this.streetViewControl,
26271             locationName: this.locationName,
26272             draggable: this.draggable,
26273             enableAutocomplete: this.enableAutocomplete,
26274             enableReverseGeocode: this.enableReverseGeocode
26275         });
26276         
26277         var _marker = new google.maps.Marker({
26278             position: position,
26279             map: _map,
26280             title: this.markerTitle,
26281             draggable: this.draggable
26282         });
26283         
26284         return {
26285             map: _map,
26286             marker: _marker,
26287             circle: null,
26288             location: position,
26289             radius: this.radius,
26290             locationName: this.locationName,
26291             addressComponents: {
26292                 formatted_address: null,
26293                 addressLine1: null,
26294                 addressLine2: null,
26295                 streetName: null,
26296                 streetNumber: null,
26297                 city: null,
26298                 district: null,
26299                 state: null,
26300                 stateOrProvince: null
26301             },
26302             settings: this,
26303             domContainer: this.el.dom,
26304             geodecoder: new google.maps.Geocoder()
26305         };
26306     },
26307     
26308     drawCircle: function(center, radius, options) 
26309     {
26310         if (this.gMapContext.circle != null) {
26311             this.gMapContext.circle.setMap(null);
26312         }
26313         if (radius > 0) {
26314             radius *= 1;
26315             options = Roo.apply({}, options, {
26316                 strokeColor: "#0000FF",
26317                 strokeOpacity: .35,
26318                 strokeWeight: 2,
26319                 fillColor: "#0000FF",
26320                 fillOpacity: .2
26321             });
26322             
26323             options.map = this.gMapContext.map;
26324             options.radius = radius;
26325             options.center = center;
26326             this.gMapContext.circle = new google.maps.Circle(options);
26327             return this.gMapContext.circle;
26328         }
26329         
26330         return null;
26331     },
26332     
26333     setPosition: function(location) 
26334     {
26335         this.gMapContext.location = location;
26336         this.gMapContext.marker.setPosition(location);
26337         this.gMapContext.map.panTo(location);
26338         this.drawCircle(location, this.gMapContext.radius, {});
26339         
26340         var _this = this;
26341         
26342         if (this.gMapContext.settings.enableReverseGeocode) {
26343             this.gMapContext.geodecoder.geocode({
26344                 latLng: this.gMapContext.location
26345             }, function(results, status) {
26346                 
26347                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26348                     _this.gMapContext.locationName = results[0].formatted_address;
26349                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26350                     
26351                     _this.fireEvent('positionchanged', this, location);
26352                 }
26353             });
26354             
26355             return;
26356         }
26357         
26358         this.fireEvent('positionchanged', this, location);
26359     },
26360     
26361     resize: function()
26362     {
26363         google.maps.event.trigger(this.gMapContext.map, "resize");
26364         
26365         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26366         
26367         this.fireEvent('resize', this);
26368     },
26369     
26370     setPositionByLatLng: function(latitude, longitude)
26371     {
26372         this.setPosition(new google.maps.LatLng(latitude, longitude));
26373     },
26374     
26375     getCurrentPosition: function() 
26376     {
26377         return {
26378             latitude: this.gMapContext.location.lat(),
26379             longitude: this.gMapContext.location.lng()
26380         };
26381     },
26382     
26383     getAddressName: function() 
26384     {
26385         return this.gMapContext.locationName;
26386     },
26387     
26388     getAddressComponents: function() 
26389     {
26390         return this.gMapContext.addressComponents;
26391     },
26392     
26393     address_component_from_google_geocode: function(address_components) 
26394     {
26395         var result = {};
26396         
26397         for (var i = 0; i < address_components.length; i++) {
26398             var component = address_components[i];
26399             if (component.types.indexOf("postal_code") >= 0) {
26400                 result.postalCode = component.short_name;
26401             } else if (component.types.indexOf("street_number") >= 0) {
26402                 result.streetNumber = component.short_name;
26403             } else if (component.types.indexOf("route") >= 0) {
26404                 result.streetName = component.short_name;
26405             } else if (component.types.indexOf("neighborhood") >= 0) {
26406                 result.city = component.short_name;
26407             } else if (component.types.indexOf("locality") >= 0) {
26408                 result.city = component.short_name;
26409             } else if (component.types.indexOf("sublocality") >= 0) {
26410                 result.district = component.short_name;
26411             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26412                 result.stateOrProvince = component.short_name;
26413             } else if (component.types.indexOf("country") >= 0) {
26414                 result.country = component.short_name;
26415             }
26416         }
26417         
26418         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26419         result.addressLine2 = "";
26420         return result;
26421     },
26422     
26423     setZoomLevel: function(zoom)
26424     {
26425         this.gMapContext.map.setZoom(zoom);
26426     },
26427     
26428     show: function()
26429     {
26430         if(!this.el){
26431             return;
26432         }
26433         
26434         this.el.show();
26435         
26436         this.resize();
26437         
26438         this.fireEvent('show', this);
26439     },
26440     
26441     hide: function()
26442     {
26443         if(!this.el){
26444             return;
26445         }
26446         
26447         this.el.hide();
26448         
26449         this.fireEvent('hide', this);
26450     }
26451     
26452 });
26453
26454 Roo.apply(Roo.bootstrap.LocationPicker, {
26455     
26456     OverlayView : function(map, options)
26457     {
26458         options = options || {};
26459         
26460         this.setMap(map);
26461     }
26462     
26463     
26464 });/*
26465  * - LGPL
26466  *
26467  * Alert
26468  * 
26469  */
26470
26471 /**
26472  * @class Roo.bootstrap.Alert
26473  * @extends Roo.bootstrap.Component
26474  * Bootstrap Alert class
26475  * @cfg {String} title The title of alert
26476  * @cfg {String} html The content of alert
26477  * @cfg {String} weight (  success | info | warning | danger )
26478  * @cfg {String} faicon font-awesomeicon
26479  * 
26480  * @constructor
26481  * Create a new alert
26482  * @param {Object} config The config object
26483  */
26484
26485
26486 Roo.bootstrap.Alert = function(config){
26487     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26488     
26489 };
26490
26491 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26492     
26493     title: '',
26494     html: '',
26495     weight: false,
26496     faicon: false,
26497     
26498     getAutoCreate : function()
26499     {
26500         
26501         var cfg = {
26502             tag : 'div',
26503             cls : 'alert',
26504             cn : [
26505                 {
26506                     tag : 'i',
26507                     cls : 'roo-alert-icon'
26508                     
26509                 },
26510                 {
26511                     tag : 'b',
26512                     cls : 'roo-alert-title',
26513                     html : this.title
26514                 },
26515                 {
26516                     tag : 'span',
26517                     cls : 'roo-alert-text',
26518                     html : this.html
26519                 }
26520             ]
26521         };
26522         
26523         if(this.faicon){
26524             cfg.cn[0].cls += ' fa ' + this.faicon;
26525         }
26526         
26527         if(this.weight){
26528             cfg.cls += ' alert-' + this.weight;
26529         }
26530         
26531         return cfg;
26532     },
26533     
26534     initEvents: function() 
26535     {
26536         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26537     },
26538     
26539     setTitle : function(str)
26540     {
26541         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26542     },
26543     
26544     setText : function(str)
26545     {
26546         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26547     },
26548     
26549     setWeight : function(weight)
26550     {
26551         if(this.weight){
26552             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26553         }
26554         
26555         this.weight = weight;
26556         
26557         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26558     },
26559     
26560     setIcon : function(icon)
26561     {
26562         if(this.faicon){
26563             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26564         }
26565         
26566         this.faicon = icon;
26567         
26568         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26569     },
26570     
26571     hide: function() 
26572     {
26573         this.el.hide();   
26574     },
26575     
26576     show: function() 
26577     {  
26578         this.el.show();   
26579     }
26580     
26581 });
26582
26583  
26584 /*
26585 * Licence: LGPL
26586 */
26587
26588 /**
26589  * @class Roo.bootstrap.UploadCropbox
26590  * @extends Roo.bootstrap.Component
26591  * Bootstrap UploadCropbox class
26592  * @cfg {String} emptyText show when image has been loaded
26593  * @cfg {String} rotateNotify show when image too small to rotate
26594  * @cfg {Number} errorTimeout default 3000
26595  * @cfg {Number} minWidth default 300
26596  * @cfg {Number} minHeight default 300
26597  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26598  * @cfg {Boolean} isDocument (true|false) default false
26599  * @cfg {String} url action url
26600  * @cfg {String} paramName default 'imageUpload'
26601  * @cfg {String} method default POST
26602  * @cfg {Boolean} loadMask (true|false) default true
26603  * @cfg {Boolean} loadingText default 'Loading...'
26604  * 
26605  * @constructor
26606  * Create a new UploadCropbox
26607  * @param {Object} config The config object
26608  */
26609
26610 Roo.bootstrap.UploadCropbox = function(config){
26611     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26612     
26613     this.addEvents({
26614         /**
26615          * @event beforeselectfile
26616          * Fire before select file
26617          * @param {Roo.bootstrap.UploadCropbox} this
26618          */
26619         "beforeselectfile" : true,
26620         /**
26621          * @event initial
26622          * Fire after initEvent
26623          * @param {Roo.bootstrap.UploadCropbox} this
26624          */
26625         "initial" : true,
26626         /**
26627          * @event crop
26628          * Fire after initEvent
26629          * @param {Roo.bootstrap.UploadCropbox} this
26630          * @param {String} data
26631          */
26632         "crop" : true,
26633         /**
26634          * @event prepare
26635          * Fire when preparing the file data
26636          * @param {Roo.bootstrap.UploadCropbox} this
26637          * @param {Object} file
26638          */
26639         "prepare" : true,
26640         /**
26641          * @event exception
26642          * Fire when get exception
26643          * @param {Roo.bootstrap.UploadCropbox} this
26644          * @param {XMLHttpRequest} xhr
26645          */
26646         "exception" : true,
26647         /**
26648          * @event beforeloadcanvas
26649          * Fire before load the canvas
26650          * @param {Roo.bootstrap.UploadCropbox} this
26651          * @param {String} src
26652          */
26653         "beforeloadcanvas" : true,
26654         /**
26655          * @event trash
26656          * Fire when trash image
26657          * @param {Roo.bootstrap.UploadCropbox} this
26658          */
26659         "trash" : true,
26660         /**
26661          * @event download
26662          * Fire when download the image
26663          * @param {Roo.bootstrap.UploadCropbox} this
26664          */
26665         "download" : true,
26666         /**
26667          * @event footerbuttonclick
26668          * Fire when footerbuttonclick
26669          * @param {Roo.bootstrap.UploadCropbox} this
26670          * @param {String} type
26671          */
26672         "footerbuttonclick" : true,
26673         /**
26674          * @event resize
26675          * Fire when resize
26676          * @param {Roo.bootstrap.UploadCropbox} this
26677          */
26678         "resize" : true,
26679         /**
26680          * @event rotate
26681          * Fire when rotate the image
26682          * @param {Roo.bootstrap.UploadCropbox} this
26683          * @param {String} pos
26684          */
26685         "rotate" : true,
26686         /**
26687          * @event inspect
26688          * Fire when inspect the file
26689          * @param {Roo.bootstrap.UploadCropbox} this
26690          * @param {Object} file
26691          */
26692         "inspect" : true,
26693         /**
26694          * @event upload
26695          * Fire when xhr upload the file
26696          * @param {Roo.bootstrap.UploadCropbox} this
26697          * @param {Object} data
26698          */
26699         "upload" : true,
26700         /**
26701          * @event arrange
26702          * Fire when arrange the file data
26703          * @param {Roo.bootstrap.UploadCropbox} this
26704          * @param {Object} formData
26705          */
26706         "arrange" : true
26707     });
26708     
26709     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26710 };
26711
26712 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26713     
26714     emptyText : 'Click to upload image',
26715     rotateNotify : 'Image is too small to rotate',
26716     errorTimeout : 3000,
26717     scale : 0,
26718     baseScale : 1,
26719     rotate : 0,
26720     dragable : false,
26721     pinching : false,
26722     mouseX : 0,
26723     mouseY : 0,
26724     cropData : false,
26725     minWidth : 300,
26726     minHeight : 300,
26727     file : false,
26728     exif : {},
26729     baseRotate : 1,
26730     cropType : 'image/jpeg',
26731     buttons : false,
26732     canvasLoaded : false,
26733     isDocument : false,
26734     method : 'POST',
26735     paramName : 'imageUpload',
26736     loadMask : true,
26737     loadingText : 'Loading...',
26738     maskEl : false,
26739     
26740     getAutoCreate : function()
26741     {
26742         var cfg = {
26743             tag : 'div',
26744             cls : 'roo-upload-cropbox',
26745             cn : [
26746                 {
26747                     tag : 'input',
26748                     cls : 'roo-upload-cropbox-selector',
26749                     type : 'file'
26750                 },
26751                 {
26752                     tag : 'div',
26753                     cls : 'roo-upload-cropbox-body',
26754                     style : 'cursor:pointer',
26755                     cn : [
26756                         {
26757                             tag : 'div',
26758                             cls : 'roo-upload-cropbox-preview'
26759                         },
26760                         {
26761                             tag : 'div',
26762                             cls : 'roo-upload-cropbox-thumb'
26763                         },
26764                         {
26765                             tag : 'div',
26766                             cls : 'roo-upload-cropbox-empty-notify',
26767                             html : this.emptyText
26768                         },
26769                         {
26770                             tag : 'div',
26771                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26772                             html : this.rotateNotify
26773                         }
26774                     ]
26775                 },
26776                 {
26777                     tag : 'div',
26778                     cls : 'roo-upload-cropbox-footer',
26779                     cn : {
26780                         tag : 'div',
26781                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26782                         cn : []
26783                     }
26784                 }
26785             ]
26786         };
26787         
26788         return cfg;
26789     },
26790     
26791     onRender : function(ct, position)
26792     {
26793         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26794         
26795         if (this.buttons.length) {
26796             
26797             Roo.each(this.buttons, function(bb) {
26798                 
26799                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26800                 
26801                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26802                 
26803             }, this);
26804         }
26805         
26806         if(this.loadMask){
26807             this.maskEl = this.el;
26808         }
26809     },
26810     
26811     initEvents : function()
26812     {
26813         this.urlAPI = (window.createObjectURL && window) || 
26814                                 (window.URL && URL.revokeObjectURL && URL) || 
26815                                 (window.webkitURL && webkitURL);
26816                         
26817         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26818         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26819         
26820         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26821         this.selectorEl.hide();
26822         
26823         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26824         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26825         
26826         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26827         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26828         this.thumbEl.hide();
26829         
26830         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26831         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26832         
26833         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26834         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26835         this.errorEl.hide();
26836         
26837         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26838         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26839         this.footerEl.hide();
26840         
26841         this.setThumbBoxSize();
26842         
26843         this.bind();
26844         
26845         this.resize();
26846         
26847         this.fireEvent('initial', this);
26848     },
26849
26850     bind : function()
26851     {
26852         var _this = this;
26853         
26854         window.addEventListener("resize", function() { _this.resize(); } );
26855         
26856         this.bodyEl.on('click', this.beforeSelectFile, this);
26857         
26858         if(Roo.isTouch){
26859             this.bodyEl.on('touchstart', this.onTouchStart, this);
26860             this.bodyEl.on('touchmove', this.onTouchMove, this);
26861             this.bodyEl.on('touchend', this.onTouchEnd, this);
26862         }
26863         
26864         if(!Roo.isTouch){
26865             this.bodyEl.on('mousedown', this.onMouseDown, this);
26866             this.bodyEl.on('mousemove', this.onMouseMove, this);
26867             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26868             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26869             Roo.get(document).on('mouseup', this.onMouseUp, this);
26870         }
26871         
26872         this.selectorEl.on('change', this.onFileSelected, this);
26873     },
26874     
26875     reset : function()
26876     {    
26877         this.scale = 0;
26878         this.baseScale = 1;
26879         this.rotate = 0;
26880         this.baseRotate = 1;
26881         this.dragable = false;
26882         this.pinching = false;
26883         this.mouseX = 0;
26884         this.mouseY = 0;
26885         this.cropData = false;
26886         this.notifyEl.dom.innerHTML = this.emptyText;
26887         
26888         this.selectorEl.dom.value = '';
26889         
26890     },
26891     
26892     resize : function()
26893     {
26894         if(this.fireEvent('resize', this) != false){
26895             this.setThumbBoxPosition();
26896             this.setCanvasPosition();
26897         }
26898     },
26899     
26900     onFooterButtonClick : function(e, el, o, type)
26901     {
26902         switch (type) {
26903             case 'rotate-left' :
26904                 this.onRotateLeft(e);
26905                 break;
26906             case 'rotate-right' :
26907                 this.onRotateRight(e);
26908                 break;
26909             case 'picture' :
26910                 this.beforeSelectFile(e);
26911                 break;
26912             case 'trash' :
26913                 this.trash(e);
26914                 break;
26915             case 'crop' :
26916                 this.crop(e);
26917                 break;
26918             case 'download' :
26919                 this.download(e);
26920                 break;
26921             default :
26922                 break;
26923         }
26924         
26925         this.fireEvent('footerbuttonclick', this, type);
26926     },
26927     
26928     beforeSelectFile : function(e)
26929     {
26930         e.preventDefault();
26931         
26932         if(this.fireEvent('beforeselectfile', this) != false){
26933             this.selectorEl.dom.click();
26934         }
26935     },
26936     
26937     onFileSelected : function(e)
26938     {
26939         e.preventDefault();
26940         
26941         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26942             return;
26943         }
26944         
26945         var file = this.selectorEl.dom.files[0];
26946         
26947         if(this.fireEvent('inspect', this, file) != false){
26948             this.prepare(file);
26949         }
26950         
26951     },
26952     
26953     trash : function(e)
26954     {
26955         this.fireEvent('trash', this);
26956     },
26957     
26958     download : function(e)
26959     {
26960         this.fireEvent('download', this);
26961     },
26962     
26963     loadCanvas : function(src)
26964     {   
26965         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26966             
26967             this.reset();
26968             
26969             this.imageEl = document.createElement('img');
26970             
26971             var _this = this;
26972             
26973             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26974             
26975             this.imageEl.src = src;
26976         }
26977     },
26978     
26979     onLoadCanvas : function()
26980     {   
26981         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26982         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26983         
26984         this.bodyEl.un('click', this.beforeSelectFile, this);
26985         
26986         this.notifyEl.hide();
26987         this.thumbEl.show();
26988         this.footerEl.show();
26989         
26990         this.baseRotateLevel();
26991         
26992         if(this.isDocument){
26993             this.setThumbBoxSize();
26994         }
26995         
26996         this.setThumbBoxPosition();
26997         
26998         this.baseScaleLevel();
26999         
27000         this.draw();
27001         
27002         this.resize();
27003         
27004         this.canvasLoaded = true;
27005         
27006         if(this.loadMask){
27007             this.maskEl.unmask();
27008         }
27009         
27010     },
27011     
27012     setCanvasPosition : function()
27013     {   
27014         if(!this.canvasEl){
27015             return;
27016         }
27017         
27018         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27019         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27020         
27021         this.previewEl.setLeft(pw);
27022         this.previewEl.setTop(ph);
27023         
27024     },
27025     
27026     onMouseDown : function(e)
27027     {   
27028         e.stopEvent();
27029         
27030         this.dragable = true;
27031         this.pinching = false;
27032         
27033         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27034             this.dragable = false;
27035             return;
27036         }
27037         
27038         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27039         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27040         
27041     },
27042     
27043     onMouseMove : function(e)
27044     {   
27045         e.stopEvent();
27046         
27047         if(!this.canvasLoaded){
27048             return;
27049         }
27050         
27051         if (!this.dragable){
27052             return;
27053         }
27054         
27055         var minX = Math.ceil(this.thumbEl.getLeft(true));
27056         var minY = Math.ceil(this.thumbEl.getTop(true));
27057         
27058         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27059         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27060         
27061         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27062         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27063         
27064         x = x - this.mouseX;
27065         y = y - this.mouseY;
27066         
27067         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27068         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27069         
27070         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27071         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27072         
27073         this.previewEl.setLeft(bgX);
27074         this.previewEl.setTop(bgY);
27075         
27076         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27077         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27078     },
27079     
27080     onMouseUp : function(e)
27081     {   
27082         e.stopEvent();
27083         
27084         this.dragable = false;
27085     },
27086     
27087     onMouseWheel : function(e)
27088     {   
27089         e.stopEvent();
27090         
27091         this.startScale = this.scale;
27092         
27093         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27094         
27095         if(!this.zoomable()){
27096             this.scale = this.startScale;
27097             return;
27098         }
27099         
27100         this.draw();
27101         
27102         return;
27103     },
27104     
27105     zoomable : function()
27106     {
27107         var minScale = this.thumbEl.getWidth() / this.minWidth;
27108         
27109         if(this.minWidth < this.minHeight){
27110             minScale = this.thumbEl.getHeight() / this.minHeight;
27111         }
27112         
27113         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27114         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27115         
27116         if(
27117                 this.isDocument &&
27118                 (this.rotate == 0 || this.rotate == 180) && 
27119                 (
27120                     width > this.imageEl.OriginWidth || 
27121                     height > this.imageEl.OriginHeight ||
27122                     (width < this.minWidth && height < this.minHeight)
27123                 )
27124         ){
27125             return false;
27126         }
27127         
27128         if(
27129                 this.isDocument &&
27130                 (this.rotate == 90 || this.rotate == 270) && 
27131                 (
27132                     width > this.imageEl.OriginWidth || 
27133                     height > this.imageEl.OriginHeight ||
27134                     (width < this.minHeight && height < this.minWidth)
27135                 )
27136         ){
27137             return false;
27138         }
27139         
27140         if(
27141                 !this.isDocument &&
27142                 (this.rotate == 0 || this.rotate == 180) && 
27143                 (
27144                     width < this.minWidth || 
27145                     width > this.imageEl.OriginWidth || 
27146                     height < this.minHeight || 
27147                     height > this.imageEl.OriginHeight
27148                 )
27149         ){
27150             return false;
27151         }
27152         
27153         if(
27154                 !this.isDocument &&
27155                 (this.rotate == 90 || this.rotate == 270) && 
27156                 (
27157                     width < this.minHeight || 
27158                     width > this.imageEl.OriginWidth || 
27159                     height < this.minWidth || 
27160                     height > this.imageEl.OriginHeight
27161                 )
27162         ){
27163             return false;
27164         }
27165         
27166         return true;
27167         
27168     },
27169     
27170     onRotateLeft : function(e)
27171     {   
27172         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27173             
27174             var minScale = this.thumbEl.getWidth() / this.minWidth;
27175             
27176             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27177             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27178             
27179             this.startScale = this.scale;
27180             
27181             while (this.getScaleLevel() < minScale){
27182             
27183                 this.scale = this.scale + 1;
27184                 
27185                 if(!this.zoomable()){
27186                     break;
27187                 }
27188                 
27189                 if(
27190                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27191                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27192                 ){
27193                     continue;
27194                 }
27195                 
27196                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27197
27198                 this.draw();
27199                 
27200                 return;
27201             }
27202             
27203             this.scale = this.startScale;
27204             
27205             this.onRotateFail();
27206             
27207             return false;
27208         }
27209         
27210         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27211
27212         if(this.isDocument){
27213             this.setThumbBoxSize();
27214             this.setThumbBoxPosition();
27215             this.setCanvasPosition();
27216         }
27217         
27218         this.draw();
27219         
27220         this.fireEvent('rotate', this, 'left');
27221         
27222     },
27223     
27224     onRotateRight : function(e)
27225     {
27226         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27227             
27228             var minScale = this.thumbEl.getWidth() / this.minWidth;
27229         
27230             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27231             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27232             
27233             this.startScale = this.scale;
27234             
27235             while (this.getScaleLevel() < minScale){
27236             
27237                 this.scale = this.scale + 1;
27238                 
27239                 if(!this.zoomable()){
27240                     break;
27241                 }
27242                 
27243                 if(
27244                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27245                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27246                 ){
27247                     continue;
27248                 }
27249                 
27250                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27251
27252                 this.draw();
27253                 
27254                 return;
27255             }
27256             
27257             this.scale = this.startScale;
27258             
27259             this.onRotateFail();
27260             
27261             return false;
27262         }
27263         
27264         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27265
27266         if(this.isDocument){
27267             this.setThumbBoxSize();
27268             this.setThumbBoxPosition();
27269             this.setCanvasPosition();
27270         }
27271         
27272         this.draw();
27273         
27274         this.fireEvent('rotate', this, 'right');
27275     },
27276     
27277     onRotateFail : function()
27278     {
27279         this.errorEl.show(true);
27280         
27281         var _this = this;
27282         
27283         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27284     },
27285     
27286     draw : function()
27287     {
27288         this.previewEl.dom.innerHTML = '';
27289         
27290         var canvasEl = document.createElement("canvas");
27291         
27292         var contextEl = canvasEl.getContext("2d");
27293         
27294         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27295         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27296         var center = this.imageEl.OriginWidth / 2;
27297         
27298         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27299             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27300             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27301             center = this.imageEl.OriginHeight / 2;
27302         }
27303         
27304         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27305         
27306         contextEl.translate(center, center);
27307         contextEl.rotate(this.rotate * Math.PI / 180);
27308
27309         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27310         
27311         this.canvasEl = document.createElement("canvas");
27312         
27313         this.contextEl = this.canvasEl.getContext("2d");
27314         
27315         switch (this.rotate) {
27316             case 0 :
27317                 
27318                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27319                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27320                 
27321                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27322                 
27323                 break;
27324             case 90 : 
27325                 
27326                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27327                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27328                 
27329                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27330                     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);
27331                     break;
27332                 }
27333                 
27334                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27335                 
27336                 break;
27337             case 180 :
27338                 
27339                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27340                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27341                 
27342                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27343                     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);
27344                     break;
27345                 }
27346                 
27347                 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);
27348                 
27349                 break;
27350             case 270 :
27351                 
27352                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27353                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27354         
27355                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27356                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27357                     break;
27358                 }
27359                 
27360                 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);
27361                 
27362                 break;
27363             default : 
27364                 break;
27365         }
27366         
27367         this.previewEl.appendChild(this.canvasEl);
27368         
27369         this.setCanvasPosition();
27370     },
27371     
27372     crop : function()
27373     {
27374         if(!this.canvasLoaded){
27375             return;
27376         }
27377         
27378         var imageCanvas = document.createElement("canvas");
27379         
27380         var imageContext = imageCanvas.getContext("2d");
27381         
27382         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27383         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27384         
27385         var center = imageCanvas.width / 2;
27386         
27387         imageContext.translate(center, center);
27388         
27389         imageContext.rotate(this.rotate * Math.PI / 180);
27390         
27391         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27392         
27393         var canvas = document.createElement("canvas");
27394         
27395         var context = canvas.getContext("2d");
27396                 
27397         canvas.width = this.minWidth;
27398         canvas.height = this.minHeight;
27399
27400         switch (this.rotate) {
27401             case 0 :
27402                 
27403                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27404                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27405                 
27406                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27407                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27408                 
27409                 var targetWidth = this.minWidth - 2 * x;
27410                 var targetHeight = this.minHeight - 2 * y;
27411                 
27412                 var scale = 1;
27413                 
27414                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27415                     scale = targetWidth / width;
27416                 }
27417                 
27418                 if(x > 0 && y == 0){
27419                     scale = targetHeight / height;
27420                 }
27421                 
27422                 if(x > 0 && y > 0){
27423                     scale = targetWidth / width;
27424                     
27425                     if(width < height){
27426                         scale = targetHeight / height;
27427                     }
27428                 }
27429                 
27430                 context.scale(scale, scale);
27431                 
27432                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27433                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27434
27435                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27436                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27437
27438                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27439                 
27440                 break;
27441             case 90 : 
27442                 
27443                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27444                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27445                 
27446                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27447                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27448                 
27449                 var targetWidth = this.minWidth - 2 * x;
27450                 var targetHeight = this.minHeight - 2 * y;
27451                 
27452                 var scale = 1;
27453                 
27454                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27455                     scale = targetWidth / width;
27456                 }
27457                 
27458                 if(x > 0 && y == 0){
27459                     scale = targetHeight / height;
27460                 }
27461                 
27462                 if(x > 0 && y > 0){
27463                     scale = targetWidth / width;
27464                     
27465                     if(width < height){
27466                         scale = targetHeight / height;
27467                     }
27468                 }
27469                 
27470                 context.scale(scale, scale);
27471                 
27472                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27473                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27474
27475                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27476                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27477                 
27478                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27479                 
27480                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27481                 
27482                 break;
27483             case 180 :
27484                 
27485                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27486                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27487                 
27488                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27489                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27490                 
27491                 var targetWidth = this.minWidth - 2 * x;
27492                 var targetHeight = this.minHeight - 2 * y;
27493                 
27494                 var scale = 1;
27495                 
27496                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27497                     scale = targetWidth / width;
27498                 }
27499                 
27500                 if(x > 0 && y == 0){
27501                     scale = targetHeight / height;
27502                 }
27503                 
27504                 if(x > 0 && y > 0){
27505                     scale = targetWidth / width;
27506                     
27507                     if(width < height){
27508                         scale = targetHeight / height;
27509                     }
27510                 }
27511                 
27512                 context.scale(scale, scale);
27513                 
27514                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27515                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27516
27517                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27518                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27519
27520                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27521                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27522                 
27523                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27524                 
27525                 break;
27526             case 270 :
27527                 
27528                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27529                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27530                 
27531                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27532                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27533                 
27534                 var targetWidth = this.minWidth - 2 * x;
27535                 var targetHeight = this.minHeight - 2 * y;
27536                 
27537                 var scale = 1;
27538                 
27539                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27540                     scale = targetWidth / width;
27541                 }
27542                 
27543                 if(x > 0 && y == 0){
27544                     scale = targetHeight / height;
27545                 }
27546                 
27547                 if(x > 0 && y > 0){
27548                     scale = targetWidth / width;
27549                     
27550                     if(width < height){
27551                         scale = targetHeight / height;
27552                     }
27553                 }
27554                 
27555                 context.scale(scale, scale);
27556                 
27557                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27558                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27559
27560                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27561                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27562                 
27563                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27564                 
27565                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27566                 
27567                 break;
27568             default : 
27569                 break;
27570         }
27571         
27572         this.cropData = canvas.toDataURL(this.cropType);
27573         
27574         if(this.fireEvent('crop', this, this.cropData) !== false){
27575             this.process(this.file, this.cropData);
27576         }
27577         
27578         return;
27579         
27580     },
27581     
27582     setThumbBoxSize : function()
27583     {
27584         var width, height;
27585         
27586         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27587             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27588             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27589             
27590             this.minWidth = width;
27591             this.minHeight = height;
27592             
27593             if(this.rotate == 90 || this.rotate == 270){
27594                 this.minWidth = height;
27595                 this.minHeight = width;
27596             }
27597         }
27598         
27599         height = 300;
27600         width = Math.ceil(this.minWidth * height / this.minHeight);
27601         
27602         if(this.minWidth > this.minHeight){
27603             width = 300;
27604             height = Math.ceil(this.minHeight * width / this.minWidth);
27605         }
27606         
27607         this.thumbEl.setStyle({
27608             width : width + 'px',
27609             height : height + 'px'
27610         });
27611
27612         return;
27613             
27614     },
27615     
27616     setThumbBoxPosition : function()
27617     {
27618         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27619         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27620         
27621         this.thumbEl.setLeft(x);
27622         this.thumbEl.setTop(y);
27623         
27624     },
27625     
27626     baseRotateLevel : function()
27627     {
27628         this.baseRotate = 1;
27629         
27630         if(
27631                 typeof(this.exif) != 'undefined' &&
27632                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27633                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27634         ){
27635             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27636         }
27637         
27638         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27639         
27640     },
27641     
27642     baseScaleLevel : function()
27643     {
27644         var width, height;
27645         
27646         if(this.isDocument){
27647             
27648             if(this.baseRotate == 6 || this.baseRotate == 8){
27649             
27650                 height = this.thumbEl.getHeight();
27651                 this.baseScale = height / this.imageEl.OriginWidth;
27652
27653                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27654                     width = this.thumbEl.getWidth();
27655                     this.baseScale = width / this.imageEl.OriginHeight;
27656                 }
27657
27658                 return;
27659             }
27660
27661             height = this.thumbEl.getHeight();
27662             this.baseScale = height / this.imageEl.OriginHeight;
27663
27664             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27665                 width = this.thumbEl.getWidth();
27666                 this.baseScale = width / this.imageEl.OriginWidth;
27667             }
27668
27669             return;
27670         }
27671         
27672         if(this.baseRotate == 6 || this.baseRotate == 8){
27673             
27674             width = this.thumbEl.getHeight();
27675             this.baseScale = width / this.imageEl.OriginHeight;
27676             
27677             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27678                 height = this.thumbEl.getWidth();
27679                 this.baseScale = height / this.imageEl.OriginHeight;
27680             }
27681             
27682             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27683                 height = this.thumbEl.getWidth();
27684                 this.baseScale = height / this.imageEl.OriginHeight;
27685                 
27686                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27687                     width = this.thumbEl.getHeight();
27688                     this.baseScale = width / this.imageEl.OriginWidth;
27689                 }
27690             }
27691             
27692             return;
27693         }
27694         
27695         width = this.thumbEl.getWidth();
27696         this.baseScale = width / this.imageEl.OriginWidth;
27697         
27698         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27699             height = this.thumbEl.getHeight();
27700             this.baseScale = height / this.imageEl.OriginHeight;
27701         }
27702         
27703         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27704             
27705             height = this.thumbEl.getHeight();
27706             this.baseScale = height / this.imageEl.OriginHeight;
27707             
27708             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27709                 width = this.thumbEl.getWidth();
27710                 this.baseScale = width / this.imageEl.OriginWidth;
27711             }
27712             
27713         }
27714         
27715         return;
27716     },
27717     
27718     getScaleLevel : function()
27719     {
27720         return this.baseScale * Math.pow(1.1, this.scale);
27721     },
27722     
27723     onTouchStart : function(e)
27724     {
27725         if(!this.canvasLoaded){
27726             this.beforeSelectFile(e);
27727             return;
27728         }
27729         
27730         var touches = e.browserEvent.touches;
27731         
27732         if(!touches){
27733             return;
27734         }
27735         
27736         if(touches.length == 1){
27737             this.onMouseDown(e);
27738             return;
27739         }
27740         
27741         if(touches.length != 2){
27742             return;
27743         }
27744         
27745         var coords = [];
27746         
27747         for(var i = 0, finger; finger = touches[i]; i++){
27748             coords.push(finger.pageX, finger.pageY);
27749         }
27750         
27751         var x = Math.pow(coords[0] - coords[2], 2);
27752         var y = Math.pow(coords[1] - coords[3], 2);
27753         
27754         this.startDistance = Math.sqrt(x + y);
27755         
27756         this.startScale = this.scale;
27757         
27758         this.pinching = true;
27759         this.dragable = false;
27760         
27761     },
27762     
27763     onTouchMove : function(e)
27764     {
27765         if(!this.pinching && !this.dragable){
27766             return;
27767         }
27768         
27769         var touches = e.browserEvent.touches;
27770         
27771         if(!touches){
27772             return;
27773         }
27774         
27775         if(this.dragable){
27776             this.onMouseMove(e);
27777             return;
27778         }
27779         
27780         var coords = [];
27781         
27782         for(var i = 0, finger; finger = touches[i]; i++){
27783             coords.push(finger.pageX, finger.pageY);
27784         }
27785         
27786         var x = Math.pow(coords[0] - coords[2], 2);
27787         var y = Math.pow(coords[1] - coords[3], 2);
27788         
27789         this.endDistance = Math.sqrt(x + y);
27790         
27791         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27792         
27793         if(!this.zoomable()){
27794             this.scale = this.startScale;
27795             return;
27796         }
27797         
27798         this.draw();
27799         
27800     },
27801     
27802     onTouchEnd : function(e)
27803     {
27804         this.pinching = false;
27805         this.dragable = false;
27806         
27807     },
27808     
27809     process : function(file, crop)
27810     {
27811         if(this.loadMask){
27812             this.maskEl.mask(this.loadingText);
27813         }
27814         
27815         this.xhr = new XMLHttpRequest();
27816         
27817         file.xhr = this.xhr;
27818
27819         this.xhr.open(this.method, this.url, true);
27820         
27821         var headers = {
27822             "Accept": "application/json",
27823             "Cache-Control": "no-cache",
27824             "X-Requested-With": "XMLHttpRequest"
27825         };
27826         
27827         for (var headerName in headers) {
27828             var headerValue = headers[headerName];
27829             if (headerValue) {
27830                 this.xhr.setRequestHeader(headerName, headerValue);
27831             }
27832         }
27833         
27834         var _this = this;
27835         
27836         this.xhr.onload = function()
27837         {
27838             _this.xhrOnLoad(_this.xhr);
27839         }
27840         
27841         this.xhr.onerror = function()
27842         {
27843             _this.xhrOnError(_this.xhr);
27844         }
27845         
27846         var formData = new FormData();
27847
27848         formData.append('returnHTML', 'NO');
27849         
27850         if(crop){
27851             formData.append('crop', crop);
27852         }
27853         
27854         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27855             formData.append(this.paramName, file, file.name);
27856         }
27857         
27858         if(typeof(file.filename) != 'undefined'){
27859             formData.append('filename', file.filename);
27860         }
27861         
27862         if(typeof(file.mimetype) != 'undefined'){
27863             formData.append('mimetype', file.mimetype);
27864         }
27865         
27866         if(this.fireEvent('arrange', this, formData) != false){
27867             this.xhr.send(formData);
27868         };
27869     },
27870     
27871     xhrOnLoad : function(xhr)
27872     {
27873         if(this.loadMask){
27874             this.maskEl.unmask();
27875         }
27876         
27877         if (xhr.readyState !== 4) {
27878             this.fireEvent('exception', this, xhr);
27879             return;
27880         }
27881
27882         var response = Roo.decode(xhr.responseText);
27883         
27884         if(!response.success){
27885             this.fireEvent('exception', this, xhr);
27886             return;
27887         }
27888         
27889         var response = Roo.decode(xhr.responseText);
27890         
27891         this.fireEvent('upload', this, response);
27892         
27893     },
27894     
27895     xhrOnError : function()
27896     {
27897         if(this.loadMask){
27898             this.maskEl.unmask();
27899         }
27900         
27901         Roo.log('xhr on error');
27902         
27903         var response = Roo.decode(xhr.responseText);
27904           
27905         Roo.log(response);
27906         
27907     },
27908     
27909     prepare : function(file)
27910     {   
27911         if(this.loadMask){
27912             this.maskEl.mask(this.loadingText);
27913         }
27914         
27915         this.file = false;
27916         this.exif = {};
27917         
27918         if(typeof(file) === 'string'){
27919             this.loadCanvas(file);
27920             return;
27921         }
27922         
27923         if(!file || !this.urlAPI){
27924             return;
27925         }
27926         
27927         this.file = file;
27928         this.cropType = file.type;
27929         
27930         var _this = this;
27931         
27932         if(this.fireEvent('prepare', this, this.file) != false){
27933             
27934             var reader = new FileReader();
27935             
27936             reader.onload = function (e) {
27937                 if (e.target.error) {
27938                     Roo.log(e.target.error);
27939                     return;
27940                 }
27941                 
27942                 var buffer = e.target.result,
27943                     dataView = new DataView(buffer),
27944                     offset = 2,
27945                     maxOffset = dataView.byteLength - 4,
27946                     markerBytes,
27947                     markerLength;
27948                 
27949                 if (dataView.getUint16(0) === 0xffd8) {
27950                     while (offset < maxOffset) {
27951                         markerBytes = dataView.getUint16(offset);
27952                         
27953                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27954                             markerLength = dataView.getUint16(offset + 2) + 2;
27955                             if (offset + markerLength > dataView.byteLength) {
27956                                 Roo.log('Invalid meta data: Invalid segment size.');
27957                                 break;
27958                             }
27959                             
27960                             if(markerBytes == 0xffe1){
27961                                 _this.parseExifData(
27962                                     dataView,
27963                                     offset,
27964                                     markerLength
27965                                 );
27966                             }
27967                             
27968                             offset += markerLength;
27969                             
27970                             continue;
27971                         }
27972                         
27973                         break;
27974                     }
27975                     
27976                 }
27977                 
27978                 var url = _this.urlAPI.createObjectURL(_this.file);
27979                 
27980                 _this.loadCanvas(url);
27981                 
27982                 return;
27983             }
27984             
27985             reader.readAsArrayBuffer(this.file);
27986             
27987         }
27988         
27989     },
27990     
27991     parseExifData : function(dataView, offset, length)
27992     {
27993         var tiffOffset = offset + 10,
27994             littleEndian,
27995             dirOffset;
27996     
27997         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27998             // No Exif data, might be XMP data instead
27999             return;
28000         }
28001         
28002         // Check for the ASCII code for "Exif" (0x45786966):
28003         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28004             // No Exif data, might be XMP data instead
28005             return;
28006         }
28007         if (tiffOffset + 8 > dataView.byteLength) {
28008             Roo.log('Invalid Exif data: Invalid segment size.');
28009             return;
28010         }
28011         // Check for the two null bytes:
28012         if (dataView.getUint16(offset + 8) !== 0x0000) {
28013             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28014             return;
28015         }
28016         // Check the byte alignment:
28017         switch (dataView.getUint16(tiffOffset)) {
28018         case 0x4949:
28019             littleEndian = true;
28020             break;
28021         case 0x4D4D:
28022             littleEndian = false;
28023             break;
28024         default:
28025             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28026             return;
28027         }
28028         // Check for the TIFF tag marker (0x002A):
28029         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28030             Roo.log('Invalid Exif data: Missing TIFF marker.');
28031             return;
28032         }
28033         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28034         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28035         
28036         this.parseExifTags(
28037             dataView,
28038             tiffOffset,
28039             tiffOffset + dirOffset,
28040             littleEndian
28041         );
28042     },
28043     
28044     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28045     {
28046         var tagsNumber,
28047             dirEndOffset,
28048             i;
28049         if (dirOffset + 6 > dataView.byteLength) {
28050             Roo.log('Invalid Exif data: Invalid directory offset.');
28051             return;
28052         }
28053         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28054         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28055         if (dirEndOffset + 4 > dataView.byteLength) {
28056             Roo.log('Invalid Exif data: Invalid directory size.');
28057             return;
28058         }
28059         for (i = 0; i < tagsNumber; i += 1) {
28060             this.parseExifTag(
28061                 dataView,
28062                 tiffOffset,
28063                 dirOffset + 2 + 12 * i, // tag offset
28064                 littleEndian
28065             );
28066         }
28067         // Return the offset to the next directory:
28068         return dataView.getUint32(dirEndOffset, littleEndian);
28069     },
28070     
28071     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28072     {
28073         var tag = dataView.getUint16(offset, littleEndian);
28074         
28075         this.exif[tag] = this.getExifValue(
28076             dataView,
28077             tiffOffset,
28078             offset,
28079             dataView.getUint16(offset + 2, littleEndian), // tag type
28080             dataView.getUint32(offset + 4, littleEndian), // tag length
28081             littleEndian
28082         );
28083     },
28084     
28085     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28086     {
28087         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28088             tagSize,
28089             dataOffset,
28090             values,
28091             i,
28092             str,
28093             c;
28094     
28095         if (!tagType) {
28096             Roo.log('Invalid Exif data: Invalid tag type.');
28097             return;
28098         }
28099         
28100         tagSize = tagType.size * length;
28101         // Determine if the value is contained in the dataOffset bytes,
28102         // or if the value at the dataOffset is a pointer to the actual data:
28103         dataOffset = tagSize > 4 ?
28104                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28105         if (dataOffset + tagSize > dataView.byteLength) {
28106             Roo.log('Invalid Exif data: Invalid data offset.');
28107             return;
28108         }
28109         if (length === 1) {
28110             return tagType.getValue(dataView, dataOffset, littleEndian);
28111         }
28112         values = [];
28113         for (i = 0; i < length; i += 1) {
28114             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28115         }
28116         
28117         if (tagType.ascii) {
28118             str = '';
28119             // Concatenate the chars:
28120             for (i = 0; i < values.length; i += 1) {
28121                 c = values[i];
28122                 // Ignore the terminating NULL byte(s):
28123                 if (c === '\u0000') {
28124                     break;
28125                 }
28126                 str += c;
28127             }
28128             return str;
28129         }
28130         return values;
28131     }
28132     
28133 });
28134
28135 Roo.apply(Roo.bootstrap.UploadCropbox, {
28136     tags : {
28137         'Orientation': 0x0112
28138     },
28139     
28140     Orientation: {
28141             1: 0, //'top-left',
28142 //            2: 'top-right',
28143             3: 180, //'bottom-right',
28144 //            4: 'bottom-left',
28145 //            5: 'left-top',
28146             6: 90, //'right-top',
28147 //            7: 'right-bottom',
28148             8: 270 //'left-bottom'
28149     },
28150     
28151     exifTagTypes : {
28152         // byte, 8-bit unsigned int:
28153         1: {
28154             getValue: function (dataView, dataOffset) {
28155                 return dataView.getUint8(dataOffset);
28156             },
28157             size: 1
28158         },
28159         // ascii, 8-bit byte:
28160         2: {
28161             getValue: function (dataView, dataOffset) {
28162                 return String.fromCharCode(dataView.getUint8(dataOffset));
28163             },
28164             size: 1,
28165             ascii: true
28166         },
28167         // short, 16 bit int:
28168         3: {
28169             getValue: function (dataView, dataOffset, littleEndian) {
28170                 return dataView.getUint16(dataOffset, littleEndian);
28171             },
28172             size: 2
28173         },
28174         // long, 32 bit int:
28175         4: {
28176             getValue: function (dataView, dataOffset, littleEndian) {
28177                 return dataView.getUint32(dataOffset, littleEndian);
28178             },
28179             size: 4
28180         },
28181         // rational = two long values, first is numerator, second is denominator:
28182         5: {
28183             getValue: function (dataView, dataOffset, littleEndian) {
28184                 return dataView.getUint32(dataOffset, littleEndian) /
28185                     dataView.getUint32(dataOffset + 4, littleEndian);
28186             },
28187             size: 8
28188         },
28189         // slong, 32 bit signed int:
28190         9: {
28191             getValue: function (dataView, dataOffset, littleEndian) {
28192                 return dataView.getInt32(dataOffset, littleEndian);
28193             },
28194             size: 4
28195         },
28196         // srational, two slongs, first is numerator, second is denominator:
28197         10: {
28198             getValue: function (dataView, dataOffset, littleEndian) {
28199                 return dataView.getInt32(dataOffset, littleEndian) /
28200                     dataView.getInt32(dataOffset + 4, littleEndian);
28201             },
28202             size: 8
28203         }
28204     },
28205     
28206     footer : {
28207         STANDARD : [
28208             {
28209                 tag : 'div',
28210                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28211                 action : 'rotate-left',
28212                 cn : [
28213                     {
28214                         tag : 'button',
28215                         cls : 'btn btn-default',
28216                         html : '<i class="fa fa-undo"></i>'
28217                     }
28218                 ]
28219             },
28220             {
28221                 tag : 'div',
28222                 cls : 'btn-group roo-upload-cropbox-picture',
28223                 action : 'picture',
28224                 cn : [
28225                     {
28226                         tag : 'button',
28227                         cls : 'btn btn-default',
28228                         html : '<i class="fa fa-picture-o"></i>'
28229                     }
28230                 ]
28231             },
28232             {
28233                 tag : 'div',
28234                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28235                 action : 'rotate-right',
28236                 cn : [
28237                     {
28238                         tag : 'button',
28239                         cls : 'btn btn-default',
28240                         html : '<i class="fa fa-repeat"></i>'
28241                     }
28242                 ]
28243             }
28244         ],
28245         DOCUMENT : [
28246             {
28247                 tag : 'div',
28248                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28249                 action : 'rotate-left',
28250                 cn : [
28251                     {
28252                         tag : 'button',
28253                         cls : 'btn btn-default',
28254                         html : '<i class="fa fa-undo"></i>'
28255                     }
28256                 ]
28257             },
28258             {
28259                 tag : 'div',
28260                 cls : 'btn-group roo-upload-cropbox-download',
28261                 action : 'download',
28262                 cn : [
28263                     {
28264                         tag : 'button',
28265                         cls : 'btn btn-default',
28266                         html : '<i class="fa fa-download"></i>'
28267                     }
28268                 ]
28269             },
28270             {
28271                 tag : 'div',
28272                 cls : 'btn-group roo-upload-cropbox-crop',
28273                 action : 'crop',
28274                 cn : [
28275                     {
28276                         tag : 'button',
28277                         cls : 'btn btn-default',
28278                         html : '<i class="fa fa-crop"></i>'
28279                     }
28280                 ]
28281             },
28282             {
28283                 tag : 'div',
28284                 cls : 'btn-group roo-upload-cropbox-trash',
28285                 action : 'trash',
28286                 cn : [
28287                     {
28288                         tag : 'button',
28289                         cls : 'btn btn-default',
28290                         html : '<i class="fa fa-trash"></i>'
28291                     }
28292                 ]
28293             },
28294             {
28295                 tag : 'div',
28296                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28297                 action : 'rotate-right',
28298                 cn : [
28299                     {
28300                         tag : 'button',
28301                         cls : 'btn btn-default',
28302                         html : '<i class="fa fa-repeat"></i>'
28303                     }
28304                 ]
28305             }
28306         ],
28307         ROTATOR : [
28308             {
28309                 tag : 'div',
28310                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28311                 action : 'rotate-left',
28312                 cn : [
28313                     {
28314                         tag : 'button',
28315                         cls : 'btn btn-default',
28316                         html : '<i class="fa fa-undo"></i>'
28317                     }
28318                 ]
28319             },
28320             {
28321                 tag : 'div',
28322                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28323                 action : 'rotate-right',
28324                 cn : [
28325                     {
28326                         tag : 'button',
28327                         cls : 'btn btn-default',
28328                         html : '<i class="fa fa-repeat"></i>'
28329                     }
28330                 ]
28331             }
28332         ]
28333     }
28334 });
28335
28336 /*
28337 * Licence: LGPL
28338 */
28339
28340 /**
28341  * @class Roo.bootstrap.DocumentManager
28342  * @extends Roo.bootstrap.Component
28343  * Bootstrap DocumentManager class
28344  * @cfg {String} paramName default 'imageUpload'
28345  * @cfg {String} toolTipName default 'filename'
28346  * @cfg {String} method default POST
28347  * @cfg {String} url action url
28348  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28349  * @cfg {Boolean} multiple multiple upload default true
28350  * @cfg {Number} thumbSize default 300
28351  * @cfg {String} fieldLabel
28352  * @cfg {Number} labelWidth default 4
28353  * @cfg {String} labelAlign (left|top) default left
28354  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28355 * @cfg {Number} labellg set the width of label (1-12)
28356  * @cfg {Number} labelmd set the width of label (1-12)
28357  * @cfg {Number} labelsm set the width of label (1-12)
28358  * @cfg {Number} labelxs set the width of label (1-12)
28359  * 
28360  * @constructor
28361  * Create a new DocumentManager
28362  * @param {Object} config The config object
28363  */
28364
28365 Roo.bootstrap.DocumentManager = function(config){
28366     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28367     
28368     this.files = [];
28369     this.delegates = [];
28370     
28371     this.addEvents({
28372         /**
28373          * @event initial
28374          * Fire when initial the DocumentManager
28375          * @param {Roo.bootstrap.DocumentManager} this
28376          */
28377         "initial" : true,
28378         /**
28379          * @event inspect
28380          * inspect selected file
28381          * @param {Roo.bootstrap.DocumentManager} this
28382          * @param {File} file
28383          */
28384         "inspect" : true,
28385         /**
28386          * @event exception
28387          * Fire when xhr load exception
28388          * @param {Roo.bootstrap.DocumentManager} this
28389          * @param {XMLHttpRequest} xhr
28390          */
28391         "exception" : true,
28392         /**
28393          * @event afterupload
28394          * Fire when xhr load exception
28395          * @param {Roo.bootstrap.DocumentManager} this
28396          * @param {XMLHttpRequest} xhr
28397          */
28398         "afterupload" : true,
28399         /**
28400          * @event prepare
28401          * prepare the form data
28402          * @param {Roo.bootstrap.DocumentManager} this
28403          * @param {Object} formData
28404          */
28405         "prepare" : true,
28406         /**
28407          * @event remove
28408          * Fire when remove the file
28409          * @param {Roo.bootstrap.DocumentManager} this
28410          * @param {Object} file
28411          */
28412         "remove" : true,
28413         /**
28414          * @event refresh
28415          * Fire after refresh the file
28416          * @param {Roo.bootstrap.DocumentManager} this
28417          */
28418         "refresh" : true,
28419         /**
28420          * @event click
28421          * Fire after click the image
28422          * @param {Roo.bootstrap.DocumentManager} this
28423          * @param {Object} file
28424          */
28425         "click" : true,
28426         /**
28427          * @event edit
28428          * Fire when upload a image and editable set to true
28429          * @param {Roo.bootstrap.DocumentManager} this
28430          * @param {Object} file
28431          */
28432         "edit" : true,
28433         /**
28434          * @event beforeselectfile
28435          * Fire before select file
28436          * @param {Roo.bootstrap.DocumentManager} this
28437          */
28438         "beforeselectfile" : true,
28439         /**
28440          * @event process
28441          * Fire before process file
28442          * @param {Roo.bootstrap.DocumentManager} this
28443          * @param {Object} file
28444          */
28445         "process" : true,
28446         /**
28447          * @event previewrendered
28448          * Fire when preview rendered
28449          * @param {Roo.bootstrap.DocumentManager} this
28450          * @param {Object} file
28451          */
28452         "previewrendered" : true
28453         
28454     });
28455 };
28456
28457 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28458     
28459     boxes : 0,
28460     inputName : '',
28461     thumbSize : 300,
28462     multiple : true,
28463     files : false,
28464     method : 'POST',
28465     url : '',
28466     paramName : 'imageUpload',
28467     toolTipName : 'filename',
28468     fieldLabel : '',
28469     labelWidth : 4,
28470     labelAlign : 'left',
28471     editable : true,
28472     delegates : false,
28473     xhr : false, 
28474     
28475     labellg : 0,
28476     labelmd : 0,
28477     labelsm : 0,
28478     labelxs : 0,
28479     
28480     getAutoCreate : function()
28481     {   
28482         var managerWidget = {
28483             tag : 'div',
28484             cls : 'roo-document-manager',
28485             cn : [
28486                 {
28487                     tag : 'input',
28488                     cls : 'roo-document-manager-selector',
28489                     type : 'file'
28490                 },
28491                 {
28492                     tag : 'div',
28493                     cls : 'roo-document-manager-uploader',
28494                     cn : [
28495                         {
28496                             tag : 'div',
28497                             cls : 'roo-document-manager-upload-btn',
28498                             html : '<i class="fa fa-plus"></i>'
28499                         }
28500                     ]
28501                     
28502                 }
28503             ]
28504         };
28505         
28506         var content = [
28507             {
28508                 tag : 'div',
28509                 cls : 'column col-md-12',
28510                 cn : managerWidget
28511             }
28512         ];
28513         
28514         if(this.fieldLabel.length){
28515             
28516             content = [
28517                 {
28518                     tag : 'div',
28519                     cls : 'column col-md-12',
28520                     html : this.fieldLabel
28521                 },
28522                 {
28523                     tag : 'div',
28524                     cls : 'column col-md-12',
28525                     cn : managerWidget
28526                 }
28527             ];
28528
28529             if(this.labelAlign == 'left'){
28530                 content = [
28531                     {
28532                         tag : 'div',
28533                         cls : 'column',
28534                         html : this.fieldLabel
28535                     },
28536                     {
28537                         tag : 'div',
28538                         cls : 'column',
28539                         cn : managerWidget
28540                     }
28541                 ];
28542                 
28543                 if(this.labelWidth > 12){
28544                     content[0].style = "width: " + this.labelWidth + 'px';
28545                 }
28546
28547                 if(this.labelWidth < 13 && this.labelmd == 0){
28548                     this.labelmd = this.labelWidth;
28549                 }
28550
28551                 if(this.labellg > 0){
28552                     content[0].cls += ' col-lg-' + this.labellg;
28553                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28554                 }
28555
28556                 if(this.labelmd > 0){
28557                     content[0].cls += ' col-md-' + this.labelmd;
28558                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28559                 }
28560
28561                 if(this.labelsm > 0){
28562                     content[0].cls += ' col-sm-' + this.labelsm;
28563                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28564                 }
28565
28566                 if(this.labelxs > 0){
28567                     content[0].cls += ' col-xs-' + this.labelxs;
28568                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28569                 }
28570                 
28571             }
28572         }
28573         
28574         var cfg = {
28575             tag : 'div',
28576             cls : 'row clearfix',
28577             cn : content
28578         };
28579         
28580         return cfg;
28581         
28582     },
28583     
28584     initEvents : function()
28585     {
28586         this.managerEl = this.el.select('.roo-document-manager', true).first();
28587         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28588         
28589         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28590         this.selectorEl.hide();
28591         
28592         if(this.multiple){
28593             this.selectorEl.attr('multiple', 'multiple');
28594         }
28595         
28596         this.selectorEl.on('change', this.onFileSelected, this);
28597         
28598         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28599         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28600         
28601         this.uploader.on('click', this.onUploaderClick, this);
28602         
28603         this.renderProgressDialog();
28604         
28605         var _this = this;
28606         
28607         window.addEventListener("resize", function() { _this.refresh(); } );
28608         
28609         this.fireEvent('initial', this);
28610     },
28611     
28612     renderProgressDialog : function()
28613     {
28614         var _this = this;
28615         
28616         this.progressDialog = new Roo.bootstrap.Modal({
28617             cls : 'roo-document-manager-progress-dialog',
28618             allow_close : false,
28619             title : '',
28620             buttons : [
28621                 {
28622                     name  :'cancel',
28623                     weight : 'danger',
28624                     html : 'Cancel'
28625                 }
28626             ], 
28627             listeners : { 
28628                 btnclick : function() {
28629                     _this.uploadCancel();
28630                     this.hide();
28631                 }
28632             }
28633         });
28634          
28635         this.progressDialog.render(Roo.get(document.body));
28636          
28637         this.progress = new Roo.bootstrap.Progress({
28638             cls : 'roo-document-manager-progress',
28639             active : true,
28640             striped : true
28641         });
28642         
28643         this.progress.render(this.progressDialog.getChildContainer());
28644         
28645         this.progressBar = new Roo.bootstrap.ProgressBar({
28646             cls : 'roo-document-manager-progress-bar',
28647             aria_valuenow : 0,
28648             aria_valuemin : 0,
28649             aria_valuemax : 12,
28650             panel : 'success'
28651         });
28652         
28653         this.progressBar.render(this.progress.getChildContainer());
28654     },
28655     
28656     onUploaderClick : function(e)
28657     {
28658         e.preventDefault();
28659      
28660         if(this.fireEvent('beforeselectfile', this) != false){
28661             this.selectorEl.dom.click();
28662         }
28663         
28664     },
28665     
28666     onFileSelected : function(e)
28667     {
28668         e.preventDefault();
28669         
28670         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28671             return;
28672         }
28673         
28674         Roo.each(this.selectorEl.dom.files, function(file){
28675             if(this.fireEvent('inspect', this, file) != false){
28676                 this.files.push(file);
28677             }
28678         }, this);
28679         
28680         this.queue();
28681         
28682     },
28683     
28684     queue : function()
28685     {
28686         this.selectorEl.dom.value = '';
28687         
28688         if(!this.files || !this.files.length){
28689             return;
28690         }
28691         
28692         if(this.boxes > 0 && this.files.length > this.boxes){
28693             this.files = this.files.slice(0, this.boxes);
28694         }
28695         
28696         this.uploader.show();
28697         
28698         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28699             this.uploader.hide();
28700         }
28701         
28702         var _this = this;
28703         
28704         var files = [];
28705         
28706         var docs = [];
28707         
28708         Roo.each(this.files, function(file){
28709             
28710             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28711                 var f = this.renderPreview(file);
28712                 files.push(f);
28713                 return;
28714             }
28715             
28716             if(file.type.indexOf('image') != -1){
28717                 this.delegates.push(
28718                     (function(){
28719                         _this.process(file);
28720                     }).createDelegate(this)
28721                 );
28722         
28723                 return;
28724             }
28725             
28726             docs.push(
28727                 (function(){
28728                     _this.process(file);
28729                 }).createDelegate(this)
28730             );
28731             
28732         }, this);
28733         
28734         this.files = files;
28735         
28736         this.delegates = this.delegates.concat(docs);
28737         
28738         if(!this.delegates.length){
28739             this.refresh();
28740             return;
28741         }
28742         
28743         this.progressBar.aria_valuemax = this.delegates.length;
28744         
28745         this.arrange();
28746         
28747         return;
28748     },
28749     
28750     arrange : function()
28751     {
28752         if(!this.delegates.length){
28753             this.progressDialog.hide();
28754             this.refresh();
28755             return;
28756         }
28757         
28758         var delegate = this.delegates.shift();
28759         
28760         this.progressDialog.show();
28761         
28762         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28763         
28764         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28765         
28766         delegate();
28767     },
28768     
28769     refresh : function()
28770     {
28771         this.uploader.show();
28772         
28773         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28774             this.uploader.hide();
28775         }
28776         
28777         Roo.isTouch ? this.closable(false) : this.closable(true);
28778         
28779         this.fireEvent('refresh', this);
28780     },
28781     
28782     onRemove : function(e, el, o)
28783     {
28784         e.preventDefault();
28785         
28786         this.fireEvent('remove', this, o);
28787         
28788     },
28789     
28790     remove : function(o)
28791     {
28792         var files = [];
28793         
28794         Roo.each(this.files, function(file){
28795             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28796                 files.push(file);
28797                 return;
28798             }
28799
28800             o.target.remove();
28801
28802         }, this);
28803         
28804         this.files = files;
28805         
28806         this.refresh();
28807     },
28808     
28809     clear : function()
28810     {
28811         Roo.each(this.files, function(file){
28812             if(!file.target){
28813                 return;
28814             }
28815             
28816             file.target.remove();
28817
28818         }, this);
28819         
28820         this.files = [];
28821         
28822         this.refresh();
28823     },
28824     
28825     onClick : function(e, el, o)
28826     {
28827         e.preventDefault();
28828         
28829         this.fireEvent('click', this, o);
28830         
28831     },
28832     
28833     closable : function(closable)
28834     {
28835         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28836             
28837             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28838             
28839             if(closable){
28840                 el.show();
28841                 return;
28842             }
28843             
28844             el.hide();
28845             
28846         }, this);
28847     },
28848     
28849     xhrOnLoad : function(xhr)
28850     {
28851         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28852             el.remove();
28853         }, this);
28854         
28855         if (xhr.readyState !== 4) {
28856             this.arrange();
28857             this.fireEvent('exception', this, xhr);
28858             return;
28859         }
28860
28861         var response = Roo.decode(xhr.responseText);
28862         
28863         if(!response.success){
28864             this.arrange();
28865             this.fireEvent('exception', this, xhr);
28866             return;
28867         }
28868         
28869         var file = this.renderPreview(response.data);
28870         
28871         this.files.push(file);
28872         
28873         this.arrange();
28874         
28875         this.fireEvent('afterupload', this, xhr);
28876         
28877     },
28878     
28879     xhrOnError : function(xhr)
28880     {
28881         Roo.log('xhr on error');
28882         
28883         var response = Roo.decode(xhr.responseText);
28884           
28885         Roo.log(response);
28886         
28887         this.arrange();
28888     },
28889     
28890     process : function(file)
28891     {
28892         if(this.fireEvent('process', this, file) !== false){
28893             if(this.editable && file.type.indexOf('image') != -1){
28894                 this.fireEvent('edit', this, file);
28895                 return;
28896             }
28897
28898             this.uploadStart(file, false);
28899
28900             return;
28901         }
28902         
28903     },
28904     
28905     uploadStart : function(file, crop)
28906     {
28907         this.xhr = new XMLHttpRequest();
28908         
28909         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28910             this.arrange();
28911             return;
28912         }
28913         
28914         file.xhr = this.xhr;
28915             
28916         this.managerEl.createChild({
28917             tag : 'div',
28918             cls : 'roo-document-manager-loading',
28919             cn : [
28920                 {
28921                     tag : 'div',
28922                     tooltip : file.name,
28923                     cls : 'roo-document-manager-thumb',
28924                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28925                 }
28926             ]
28927
28928         });
28929
28930         this.xhr.open(this.method, this.url, true);
28931         
28932         var headers = {
28933             "Accept": "application/json",
28934             "Cache-Control": "no-cache",
28935             "X-Requested-With": "XMLHttpRequest"
28936         };
28937         
28938         for (var headerName in headers) {
28939             var headerValue = headers[headerName];
28940             if (headerValue) {
28941                 this.xhr.setRequestHeader(headerName, headerValue);
28942             }
28943         }
28944         
28945         var _this = this;
28946         
28947         this.xhr.onload = function()
28948         {
28949             _this.xhrOnLoad(_this.xhr);
28950         }
28951         
28952         this.xhr.onerror = function()
28953         {
28954             _this.xhrOnError(_this.xhr);
28955         }
28956         
28957         var formData = new FormData();
28958
28959         formData.append('returnHTML', 'NO');
28960         
28961         if(crop){
28962             formData.append('crop', crop);
28963         }
28964         
28965         formData.append(this.paramName, file, file.name);
28966         
28967         var options = {
28968             file : file, 
28969             manually : false
28970         };
28971         
28972         if(this.fireEvent('prepare', this, formData, options) != false){
28973             
28974             if(options.manually){
28975                 return;
28976             }
28977             
28978             this.xhr.send(formData);
28979             return;
28980         };
28981         
28982         this.uploadCancel();
28983     },
28984     
28985     uploadCancel : function()
28986     {
28987         if (this.xhr) {
28988             this.xhr.abort();
28989         }
28990         
28991         this.delegates = [];
28992         
28993         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28994             el.remove();
28995         }, this);
28996         
28997         this.arrange();
28998     },
28999     
29000     renderPreview : function(file)
29001     {
29002         if(typeof(file.target) != 'undefined' && file.target){
29003             return file;
29004         }
29005         
29006         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29007         
29008         var previewEl = this.managerEl.createChild({
29009             tag : 'div',
29010             cls : 'roo-document-manager-preview',
29011             cn : [
29012                 {
29013                     tag : 'div',
29014                     tooltip : file[this.toolTipName],
29015                     cls : 'roo-document-manager-thumb',
29016                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29017                 },
29018                 {
29019                     tag : 'button',
29020                     cls : 'close',
29021                     html : '<i class="fa fa-times-circle"></i>'
29022                 }
29023             ]
29024         });
29025
29026         var close = previewEl.select('button.close', true).first();
29027
29028         close.on('click', this.onRemove, this, file);
29029
29030         file.target = previewEl;
29031
29032         var image = previewEl.select('img', true).first();
29033         
29034         var _this = this;
29035         
29036         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29037         
29038         image.on('click', this.onClick, this, file);
29039         
29040         this.fireEvent('previewrendered', this, file);
29041         
29042         return file;
29043         
29044     },
29045     
29046     onPreviewLoad : function(file, image)
29047     {
29048         if(typeof(file.target) == 'undefined' || !file.target){
29049             return;
29050         }
29051         
29052         var width = image.dom.naturalWidth || image.dom.width;
29053         var height = image.dom.naturalHeight || image.dom.height;
29054         
29055         if(width > height){
29056             file.target.addClass('wide');
29057             return;
29058         }
29059         
29060         file.target.addClass('tall');
29061         return;
29062         
29063     },
29064     
29065     uploadFromSource : function(file, crop)
29066     {
29067         this.xhr = new XMLHttpRequest();
29068         
29069         this.managerEl.createChild({
29070             tag : 'div',
29071             cls : 'roo-document-manager-loading',
29072             cn : [
29073                 {
29074                     tag : 'div',
29075                     tooltip : file.name,
29076                     cls : 'roo-document-manager-thumb',
29077                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29078                 }
29079             ]
29080
29081         });
29082
29083         this.xhr.open(this.method, this.url, true);
29084         
29085         var headers = {
29086             "Accept": "application/json",
29087             "Cache-Control": "no-cache",
29088             "X-Requested-With": "XMLHttpRequest"
29089         };
29090         
29091         for (var headerName in headers) {
29092             var headerValue = headers[headerName];
29093             if (headerValue) {
29094                 this.xhr.setRequestHeader(headerName, headerValue);
29095             }
29096         }
29097         
29098         var _this = this;
29099         
29100         this.xhr.onload = function()
29101         {
29102             _this.xhrOnLoad(_this.xhr);
29103         }
29104         
29105         this.xhr.onerror = function()
29106         {
29107             _this.xhrOnError(_this.xhr);
29108         }
29109         
29110         var formData = new FormData();
29111
29112         formData.append('returnHTML', 'NO');
29113         
29114         formData.append('crop', crop);
29115         
29116         if(typeof(file.filename) != 'undefined'){
29117             formData.append('filename', file.filename);
29118         }
29119         
29120         if(typeof(file.mimetype) != 'undefined'){
29121             formData.append('mimetype', file.mimetype);
29122         }
29123         
29124         Roo.log(formData);
29125         
29126         if(this.fireEvent('prepare', this, formData) != false){
29127             this.xhr.send(formData);
29128         };
29129     }
29130 });
29131
29132 /*
29133 * Licence: LGPL
29134 */
29135
29136 /**
29137  * @class Roo.bootstrap.DocumentViewer
29138  * @extends Roo.bootstrap.Component
29139  * Bootstrap DocumentViewer class
29140  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29141  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29142  * 
29143  * @constructor
29144  * Create a new DocumentViewer
29145  * @param {Object} config The config object
29146  */
29147
29148 Roo.bootstrap.DocumentViewer = function(config){
29149     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29150     
29151     this.addEvents({
29152         /**
29153          * @event initial
29154          * Fire after initEvent
29155          * @param {Roo.bootstrap.DocumentViewer} this
29156          */
29157         "initial" : true,
29158         /**
29159          * @event click
29160          * Fire after click
29161          * @param {Roo.bootstrap.DocumentViewer} this
29162          */
29163         "click" : true,
29164         /**
29165          * @event download
29166          * Fire after download button
29167          * @param {Roo.bootstrap.DocumentViewer} this
29168          */
29169         "download" : true,
29170         /**
29171          * @event trash
29172          * Fire after trash button
29173          * @param {Roo.bootstrap.DocumentViewer} this
29174          */
29175         "trash" : true
29176         
29177     });
29178 };
29179
29180 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29181     
29182     showDownload : true,
29183     
29184     showTrash : true,
29185     
29186     getAutoCreate : function()
29187     {
29188         var cfg = {
29189             tag : 'div',
29190             cls : 'roo-document-viewer',
29191             cn : [
29192                 {
29193                     tag : 'div',
29194                     cls : 'roo-document-viewer-body',
29195                     cn : [
29196                         {
29197                             tag : 'div',
29198                             cls : 'roo-document-viewer-thumb',
29199                             cn : [
29200                                 {
29201                                     tag : 'img',
29202                                     cls : 'roo-document-viewer-image'
29203                                 }
29204                             ]
29205                         }
29206                     ]
29207                 },
29208                 {
29209                     tag : 'div',
29210                     cls : 'roo-document-viewer-footer',
29211                     cn : {
29212                         tag : 'div',
29213                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29214                         cn : [
29215                             {
29216                                 tag : 'div',
29217                                 cls : 'btn-group roo-document-viewer-download',
29218                                 cn : [
29219                                     {
29220                                         tag : 'button',
29221                                         cls : 'btn btn-default',
29222                                         html : '<i class="fa fa-download"></i>'
29223                                     }
29224                                 ]
29225                             },
29226                             {
29227                                 tag : 'div',
29228                                 cls : 'btn-group roo-document-viewer-trash',
29229                                 cn : [
29230                                     {
29231                                         tag : 'button',
29232                                         cls : 'btn btn-default',
29233                                         html : '<i class="fa fa-trash"></i>'
29234                                     }
29235                                 ]
29236                             }
29237                         ]
29238                     }
29239                 }
29240             ]
29241         };
29242         
29243         return cfg;
29244     },
29245     
29246     initEvents : function()
29247     {
29248         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29249         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29250         
29251         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29252         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29253         
29254         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29255         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29256         
29257         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29258         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29259         
29260         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29261         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29262         
29263         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29264         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29265         
29266         this.bodyEl.on('click', this.onClick, this);
29267         this.downloadBtn.on('click', this.onDownload, this);
29268         this.trashBtn.on('click', this.onTrash, this);
29269         
29270         this.downloadBtn.hide();
29271         this.trashBtn.hide();
29272         
29273         if(this.showDownload){
29274             this.downloadBtn.show();
29275         }
29276         
29277         if(this.showTrash){
29278             this.trashBtn.show();
29279         }
29280         
29281         if(!this.showDownload && !this.showTrash) {
29282             this.footerEl.hide();
29283         }
29284         
29285     },
29286     
29287     initial : function()
29288     {
29289         this.fireEvent('initial', this);
29290         
29291     },
29292     
29293     onClick : function(e)
29294     {
29295         e.preventDefault();
29296         
29297         this.fireEvent('click', this);
29298     },
29299     
29300     onDownload : function(e)
29301     {
29302         e.preventDefault();
29303         
29304         this.fireEvent('download', this);
29305     },
29306     
29307     onTrash : function(e)
29308     {
29309         e.preventDefault();
29310         
29311         this.fireEvent('trash', this);
29312     }
29313     
29314 });
29315 /*
29316  * - LGPL
29317  *
29318  * nav progress bar
29319  * 
29320  */
29321
29322 /**
29323  * @class Roo.bootstrap.NavProgressBar
29324  * @extends Roo.bootstrap.Component
29325  * Bootstrap NavProgressBar class
29326  * 
29327  * @constructor
29328  * Create a new nav progress bar
29329  * @param {Object} config The config object
29330  */
29331
29332 Roo.bootstrap.NavProgressBar = function(config){
29333     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29334
29335     this.bullets = this.bullets || [];
29336    
29337 //    Roo.bootstrap.NavProgressBar.register(this);
29338      this.addEvents({
29339         /**
29340              * @event changed
29341              * Fires when the active item changes
29342              * @param {Roo.bootstrap.NavProgressBar} this
29343              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29344              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29345          */
29346         'changed': true
29347      });
29348     
29349 };
29350
29351 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29352     
29353     bullets : [],
29354     barItems : [],
29355     
29356     getAutoCreate : function()
29357     {
29358         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29359         
29360         cfg = {
29361             tag : 'div',
29362             cls : 'roo-navigation-bar-group',
29363             cn : [
29364                 {
29365                     tag : 'div',
29366                     cls : 'roo-navigation-top-bar'
29367                 },
29368                 {
29369                     tag : 'div',
29370                     cls : 'roo-navigation-bullets-bar',
29371                     cn : [
29372                         {
29373                             tag : 'ul',
29374                             cls : 'roo-navigation-bar'
29375                         }
29376                     ]
29377                 },
29378                 
29379                 {
29380                     tag : 'div',
29381                     cls : 'roo-navigation-bottom-bar'
29382                 }
29383             ]
29384             
29385         };
29386         
29387         return cfg;
29388         
29389     },
29390     
29391     initEvents: function() 
29392     {
29393         
29394     },
29395     
29396     onRender : function(ct, position) 
29397     {
29398         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29399         
29400         if(this.bullets.length){
29401             Roo.each(this.bullets, function(b){
29402                this.addItem(b);
29403             }, this);
29404         }
29405         
29406         this.format();
29407         
29408     },
29409     
29410     addItem : function(cfg)
29411     {
29412         var item = new Roo.bootstrap.NavProgressItem(cfg);
29413         
29414         item.parentId = this.id;
29415         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29416         
29417         if(cfg.html){
29418             var top = new Roo.bootstrap.Element({
29419                 tag : 'div',
29420                 cls : 'roo-navigation-bar-text'
29421             });
29422             
29423             var bottom = new Roo.bootstrap.Element({
29424                 tag : 'div',
29425                 cls : 'roo-navigation-bar-text'
29426             });
29427             
29428             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29429             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29430             
29431             var topText = new Roo.bootstrap.Element({
29432                 tag : 'span',
29433                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29434             });
29435             
29436             var bottomText = new Roo.bootstrap.Element({
29437                 tag : 'span',
29438                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29439             });
29440             
29441             topText.onRender(top.el, null);
29442             bottomText.onRender(bottom.el, null);
29443             
29444             item.topEl = top;
29445             item.bottomEl = bottom;
29446         }
29447         
29448         this.barItems.push(item);
29449         
29450         return item;
29451     },
29452     
29453     getActive : function()
29454     {
29455         var active = false;
29456         
29457         Roo.each(this.barItems, function(v){
29458             
29459             if (!v.isActive()) {
29460                 return;
29461             }
29462             
29463             active = v;
29464             return false;
29465             
29466         });
29467         
29468         return active;
29469     },
29470     
29471     setActiveItem : function(item)
29472     {
29473         var prev = false;
29474         
29475         Roo.each(this.barItems, function(v){
29476             if (v.rid == item.rid) {
29477                 return ;
29478             }
29479             
29480             if (v.isActive()) {
29481                 v.setActive(false);
29482                 prev = v;
29483             }
29484         });
29485
29486         item.setActive(true);
29487         
29488         this.fireEvent('changed', this, item, prev);
29489     },
29490     
29491     getBarItem: function(rid)
29492     {
29493         var ret = false;
29494         
29495         Roo.each(this.barItems, function(e) {
29496             if (e.rid != rid) {
29497                 return;
29498             }
29499             
29500             ret =  e;
29501             return false;
29502         });
29503         
29504         return ret;
29505     },
29506     
29507     indexOfItem : function(item)
29508     {
29509         var index = false;
29510         
29511         Roo.each(this.barItems, function(v, i){
29512             
29513             if (v.rid != item.rid) {
29514                 return;
29515             }
29516             
29517             index = i;
29518             return false
29519         });
29520         
29521         return index;
29522     },
29523     
29524     setActiveNext : function()
29525     {
29526         var i = this.indexOfItem(this.getActive());
29527         
29528         if (i > this.barItems.length) {
29529             return;
29530         }
29531         
29532         this.setActiveItem(this.barItems[i+1]);
29533     },
29534     
29535     setActivePrev : function()
29536     {
29537         var i = this.indexOfItem(this.getActive());
29538         
29539         if (i  < 1) {
29540             return;
29541         }
29542         
29543         this.setActiveItem(this.barItems[i-1]);
29544     },
29545     
29546     format : function()
29547     {
29548         if(!this.barItems.length){
29549             return;
29550         }
29551      
29552         var width = 100 / this.barItems.length;
29553         
29554         Roo.each(this.barItems, function(i){
29555             i.el.setStyle('width', width + '%');
29556             i.topEl.el.setStyle('width', width + '%');
29557             i.bottomEl.el.setStyle('width', width + '%');
29558         }, this);
29559         
29560     }
29561     
29562 });
29563 /*
29564  * - LGPL
29565  *
29566  * Nav Progress Item
29567  * 
29568  */
29569
29570 /**
29571  * @class Roo.bootstrap.NavProgressItem
29572  * @extends Roo.bootstrap.Component
29573  * Bootstrap NavProgressItem class
29574  * @cfg {String} rid the reference id
29575  * @cfg {Boolean} active (true|false) Is item active default false
29576  * @cfg {Boolean} disabled (true|false) Is item active default false
29577  * @cfg {String} html
29578  * @cfg {String} position (top|bottom) text position default bottom
29579  * @cfg {String} icon show icon instead of number
29580  * 
29581  * @constructor
29582  * Create a new NavProgressItem
29583  * @param {Object} config The config object
29584  */
29585 Roo.bootstrap.NavProgressItem = function(config){
29586     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29587     this.addEvents({
29588         // raw events
29589         /**
29590          * @event click
29591          * The raw click event for the entire grid.
29592          * @param {Roo.bootstrap.NavProgressItem} this
29593          * @param {Roo.EventObject} e
29594          */
29595         "click" : true
29596     });
29597    
29598 };
29599
29600 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29601     
29602     rid : '',
29603     active : false,
29604     disabled : false,
29605     html : '',
29606     position : 'bottom',
29607     icon : false,
29608     
29609     getAutoCreate : function()
29610     {
29611         var iconCls = 'roo-navigation-bar-item-icon';
29612         
29613         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29614         
29615         var cfg = {
29616             tag: 'li',
29617             cls: 'roo-navigation-bar-item',
29618             cn : [
29619                 {
29620                     tag : 'i',
29621                     cls : iconCls
29622                 }
29623             ]
29624         };
29625         
29626         if(this.active){
29627             cfg.cls += ' active';
29628         }
29629         if(this.disabled){
29630             cfg.cls += ' disabled';
29631         }
29632         
29633         return cfg;
29634     },
29635     
29636     disable : function()
29637     {
29638         this.setDisabled(true);
29639     },
29640     
29641     enable : function()
29642     {
29643         this.setDisabled(false);
29644     },
29645     
29646     initEvents: function() 
29647     {
29648         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29649         
29650         this.iconEl.on('click', this.onClick, this);
29651     },
29652     
29653     onClick : function(e)
29654     {
29655         e.preventDefault();
29656         
29657         if(this.disabled){
29658             return;
29659         }
29660         
29661         if(this.fireEvent('click', this, e) === false){
29662             return;
29663         };
29664         
29665         this.parent().setActiveItem(this);
29666     },
29667     
29668     isActive: function () 
29669     {
29670         return this.active;
29671     },
29672     
29673     setActive : function(state)
29674     {
29675         if(this.active == state){
29676             return;
29677         }
29678         
29679         this.active = state;
29680         
29681         if (state) {
29682             this.el.addClass('active');
29683             return;
29684         }
29685         
29686         this.el.removeClass('active');
29687         
29688         return;
29689     },
29690     
29691     setDisabled : function(state)
29692     {
29693         if(this.disabled == state){
29694             return;
29695         }
29696         
29697         this.disabled = state;
29698         
29699         if (state) {
29700             this.el.addClass('disabled');
29701             return;
29702         }
29703         
29704         this.el.removeClass('disabled');
29705     },
29706     
29707     tooltipEl : function()
29708     {
29709         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29710     }
29711 });
29712  
29713
29714  /*
29715  * - LGPL
29716  *
29717  * FieldLabel
29718  * 
29719  */
29720
29721 /**
29722  * @class Roo.bootstrap.FieldLabel
29723  * @extends Roo.bootstrap.Component
29724  * Bootstrap FieldLabel class
29725  * @cfg {String} html contents of the element
29726  * @cfg {String} tag tag of the element default label
29727  * @cfg {String} cls class of the element
29728  * @cfg {String} target label target 
29729  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29730  * @cfg {String} invalidClass default "text-warning"
29731  * @cfg {String} validClass default "text-success"
29732  * @cfg {String} iconTooltip default "This field is required"
29733  * @cfg {String} indicatorpos (left|right) default left
29734  * 
29735  * @constructor
29736  * Create a new FieldLabel
29737  * @param {Object} config The config object
29738  */
29739
29740 Roo.bootstrap.FieldLabel = function(config){
29741     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29742     
29743     this.addEvents({
29744             /**
29745              * @event invalid
29746              * Fires after the field has been marked as invalid.
29747              * @param {Roo.form.FieldLabel} this
29748              * @param {String} msg The validation message
29749              */
29750             invalid : true,
29751             /**
29752              * @event valid
29753              * Fires after the field has been validated with no errors.
29754              * @param {Roo.form.FieldLabel} this
29755              */
29756             valid : true
29757         });
29758 };
29759
29760 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29761     
29762     tag: 'label',
29763     cls: '',
29764     html: '',
29765     target: '',
29766     allowBlank : true,
29767     invalidClass : 'has-warning',
29768     validClass : 'has-success',
29769     iconTooltip : 'This field is required',
29770     indicatorpos : 'left',
29771     
29772     getAutoCreate : function(){
29773         
29774         var cfg = {
29775             tag : this.tag,
29776             cls : 'roo-bootstrap-field-label ' + this.cls,
29777             for : this.target,
29778             cn : [
29779                 {
29780                     tag : 'i',
29781                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29782                     tooltip : this.iconTooltip
29783                 },
29784                 {
29785                     tag : 'span',
29786                     html : this.html
29787                 }
29788             ] 
29789         };
29790         
29791         if(this.indicatorpos == 'right'){
29792             var cfg = {
29793                 tag : this.tag,
29794                 cls : 'roo-bootstrap-field-label ' + this.cls,
29795                 for : this.target,
29796                 cn : [
29797                     {
29798                         tag : 'span',
29799                         html : this.html
29800                     },
29801                     {
29802                         tag : 'i',
29803                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29804                         tooltip : this.iconTooltip
29805                     }
29806                 ] 
29807             };
29808         }
29809         
29810         return cfg;
29811     },
29812     
29813     initEvents: function() 
29814     {
29815         Roo.bootstrap.Element.superclass.initEvents.call(this);
29816         
29817         this.indicator = this.indicatorEl();
29818         
29819         if(this.indicator){
29820             this.indicator.removeClass('visible');
29821             this.indicator.addClass('invisible');
29822         }
29823         
29824         Roo.bootstrap.FieldLabel.register(this);
29825     },
29826     
29827     indicatorEl : function()
29828     {
29829         var indicator = this.el.select('i.roo-required-indicator',true).first();
29830         
29831         if(!indicator){
29832             return false;
29833         }
29834         
29835         return indicator;
29836         
29837     },
29838     
29839     /**
29840      * Mark this field as valid
29841      */
29842     markValid : function()
29843     {
29844         if(this.indicator){
29845             this.indicator.removeClass('visible');
29846             this.indicator.addClass('invisible');
29847         }
29848         
29849         this.el.removeClass(this.invalidClass);
29850         
29851         this.el.addClass(this.validClass);
29852         
29853         this.fireEvent('valid', this);
29854     },
29855     
29856     /**
29857      * Mark this field as invalid
29858      * @param {String} msg The validation message
29859      */
29860     markInvalid : function(msg)
29861     {
29862         if(this.indicator){
29863             this.indicator.removeClass('invisible');
29864             this.indicator.addClass('visible');
29865         }
29866         
29867         this.el.removeClass(this.validClass);
29868         
29869         this.el.addClass(this.invalidClass);
29870         
29871         this.fireEvent('invalid', this, msg);
29872     }
29873     
29874    
29875 });
29876
29877 Roo.apply(Roo.bootstrap.FieldLabel, {
29878     
29879     groups: {},
29880     
29881      /**
29882     * register a FieldLabel Group
29883     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29884     */
29885     register : function(label)
29886     {
29887         if(this.groups.hasOwnProperty(label.target)){
29888             return;
29889         }
29890      
29891         this.groups[label.target] = label;
29892         
29893     },
29894     /**
29895     * fetch a FieldLabel Group based on the target
29896     * @param {string} target
29897     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29898     */
29899     get: function(target) {
29900         if (typeof(this.groups[target]) == 'undefined') {
29901             return false;
29902         }
29903         
29904         return this.groups[target] ;
29905     }
29906 });
29907
29908  
29909
29910  /*
29911  * - LGPL
29912  *
29913  * page DateSplitField.
29914  * 
29915  */
29916
29917
29918 /**
29919  * @class Roo.bootstrap.DateSplitField
29920  * @extends Roo.bootstrap.Component
29921  * Bootstrap DateSplitField class
29922  * @cfg {string} fieldLabel - the label associated
29923  * @cfg {Number} labelWidth set the width of label (0-12)
29924  * @cfg {String} labelAlign (top|left)
29925  * @cfg {Boolean} dayAllowBlank (true|false) default false
29926  * @cfg {Boolean} monthAllowBlank (true|false) default false
29927  * @cfg {Boolean} yearAllowBlank (true|false) default false
29928  * @cfg {string} dayPlaceholder 
29929  * @cfg {string} monthPlaceholder
29930  * @cfg {string} yearPlaceholder
29931  * @cfg {string} dayFormat default 'd'
29932  * @cfg {string} monthFormat default 'm'
29933  * @cfg {string} yearFormat default 'Y'
29934  * @cfg {Number} labellg set the width of label (1-12)
29935  * @cfg {Number} labelmd set the width of label (1-12)
29936  * @cfg {Number} labelsm set the width of label (1-12)
29937  * @cfg {Number} labelxs set the width of label (1-12)
29938
29939  *     
29940  * @constructor
29941  * Create a new DateSplitField
29942  * @param {Object} config The config object
29943  */
29944
29945 Roo.bootstrap.DateSplitField = function(config){
29946     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29947     
29948     this.addEvents({
29949         // raw events
29950          /**
29951          * @event years
29952          * getting the data of years
29953          * @param {Roo.bootstrap.DateSplitField} this
29954          * @param {Object} years
29955          */
29956         "years" : true,
29957         /**
29958          * @event days
29959          * getting the data of days
29960          * @param {Roo.bootstrap.DateSplitField} this
29961          * @param {Object} days
29962          */
29963         "days" : true,
29964         /**
29965          * @event invalid
29966          * Fires after the field has been marked as invalid.
29967          * @param {Roo.form.Field} this
29968          * @param {String} msg The validation message
29969          */
29970         invalid : true,
29971        /**
29972          * @event valid
29973          * Fires after the field has been validated with no errors.
29974          * @param {Roo.form.Field} this
29975          */
29976         valid : true
29977     });
29978 };
29979
29980 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29981     
29982     fieldLabel : '',
29983     labelAlign : 'top',
29984     labelWidth : 3,
29985     dayAllowBlank : false,
29986     monthAllowBlank : false,
29987     yearAllowBlank : false,
29988     dayPlaceholder : '',
29989     monthPlaceholder : '',
29990     yearPlaceholder : '',
29991     dayFormat : 'd',
29992     monthFormat : 'm',
29993     yearFormat : 'Y',
29994     isFormField : true,
29995     labellg : 0,
29996     labelmd : 0,
29997     labelsm : 0,
29998     labelxs : 0,
29999     
30000     getAutoCreate : function()
30001     {
30002         var cfg = {
30003             tag : 'div',
30004             cls : 'row roo-date-split-field-group',
30005             cn : [
30006                 {
30007                     tag : 'input',
30008                     type : 'hidden',
30009                     cls : 'form-hidden-field roo-date-split-field-group-value',
30010                     name : this.name
30011                 }
30012             ]
30013         };
30014         
30015         var labelCls = 'col-md-12';
30016         var contentCls = 'col-md-4';
30017         
30018         if(this.fieldLabel){
30019             
30020             var label = {
30021                 tag : 'div',
30022                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30023                 cn : [
30024                     {
30025                         tag : 'label',
30026                         html : this.fieldLabel
30027                     }
30028                 ]
30029             };
30030             
30031             if(this.labelAlign == 'left'){
30032             
30033                 if(this.labelWidth > 12){
30034                     label.style = "width: " + this.labelWidth + 'px';
30035                 }
30036
30037                 if(this.labelWidth < 13 && this.labelmd == 0){
30038                     this.labelmd = this.labelWidth;
30039                 }
30040
30041                 if(this.labellg > 0){
30042                     labelCls = ' col-lg-' + this.labellg;
30043                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30044                 }
30045
30046                 if(this.labelmd > 0){
30047                     labelCls = ' col-md-' + this.labelmd;
30048                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30049                 }
30050
30051                 if(this.labelsm > 0){
30052                     labelCls = ' col-sm-' + this.labelsm;
30053                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30054                 }
30055
30056                 if(this.labelxs > 0){
30057                     labelCls = ' col-xs-' + this.labelxs;
30058                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30059                 }
30060             }
30061             
30062             label.cls += ' ' + labelCls;
30063             
30064             cfg.cn.push(label);
30065         }
30066         
30067         Roo.each(['day', 'month', 'year'], function(t){
30068             cfg.cn.push({
30069                 tag : 'div',
30070                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30071             });
30072         }, this);
30073         
30074         return cfg;
30075     },
30076     
30077     inputEl: function ()
30078     {
30079         return this.el.select('.roo-date-split-field-group-value', true).first();
30080     },
30081     
30082     onRender : function(ct, position) 
30083     {
30084         var _this = this;
30085         
30086         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30087         
30088         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30089         
30090         this.dayField = new Roo.bootstrap.ComboBox({
30091             allowBlank : this.dayAllowBlank,
30092             alwaysQuery : true,
30093             displayField : 'value',
30094             editable : false,
30095             fieldLabel : '',
30096             forceSelection : true,
30097             mode : 'local',
30098             placeholder : this.dayPlaceholder,
30099             selectOnFocus : true,
30100             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30101             triggerAction : 'all',
30102             typeAhead : true,
30103             valueField : 'value',
30104             store : new Roo.data.SimpleStore({
30105                 data : (function() {    
30106                     var days = [];
30107                     _this.fireEvent('days', _this, days);
30108                     return days;
30109                 })(),
30110                 fields : [ 'value' ]
30111             }),
30112             listeners : {
30113                 select : function (_self, record, index)
30114                 {
30115                     _this.setValue(_this.getValue());
30116                 }
30117             }
30118         });
30119
30120         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30121         
30122         this.monthField = new Roo.bootstrap.MonthField({
30123             after : '<i class=\"fa fa-calendar\"></i>',
30124             allowBlank : this.monthAllowBlank,
30125             placeholder : this.monthPlaceholder,
30126             readOnly : true,
30127             listeners : {
30128                 render : function (_self)
30129                 {
30130                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30131                         e.preventDefault();
30132                         _self.focus();
30133                     });
30134                 },
30135                 select : function (_self, oldvalue, newvalue)
30136                 {
30137                     _this.setValue(_this.getValue());
30138                 }
30139             }
30140         });
30141         
30142         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30143         
30144         this.yearField = new Roo.bootstrap.ComboBox({
30145             allowBlank : this.yearAllowBlank,
30146             alwaysQuery : true,
30147             displayField : 'value',
30148             editable : false,
30149             fieldLabel : '',
30150             forceSelection : true,
30151             mode : 'local',
30152             placeholder : this.yearPlaceholder,
30153             selectOnFocus : true,
30154             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30155             triggerAction : 'all',
30156             typeAhead : true,
30157             valueField : 'value',
30158             store : new Roo.data.SimpleStore({
30159                 data : (function() {
30160                     var years = [];
30161                     _this.fireEvent('years', _this, years);
30162                     return years;
30163                 })(),
30164                 fields : [ 'value' ]
30165             }),
30166             listeners : {
30167                 select : function (_self, record, index)
30168                 {
30169                     _this.setValue(_this.getValue());
30170                 }
30171             }
30172         });
30173
30174         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30175     },
30176     
30177     setValue : function(v, format)
30178     {
30179         this.inputEl.dom.value = v;
30180         
30181         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30182         
30183         var d = Date.parseDate(v, f);
30184         
30185         if(!d){
30186             this.validate();
30187             return;
30188         }
30189         
30190         this.setDay(d.format(this.dayFormat));
30191         this.setMonth(d.format(this.monthFormat));
30192         this.setYear(d.format(this.yearFormat));
30193         
30194         this.validate();
30195         
30196         return;
30197     },
30198     
30199     setDay : function(v)
30200     {
30201         this.dayField.setValue(v);
30202         this.inputEl.dom.value = this.getValue();
30203         this.validate();
30204         return;
30205     },
30206     
30207     setMonth : function(v)
30208     {
30209         this.monthField.setValue(v, true);
30210         this.inputEl.dom.value = this.getValue();
30211         this.validate();
30212         return;
30213     },
30214     
30215     setYear : function(v)
30216     {
30217         this.yearField.setValue(v);
30218         this.inputEl.dom.value = this.getValue();
30219         this.validate();
30220         return;
30221     },
30222     
30223     getDay : function()
30224     {
30225         return this.dayField.getValue();
30226     },
30227     
30228     getMonth : function()
30229     {
30230         return this.monthField.getValue();
30231     },
30232     
30233     getYear : function()
30234     {
30235         return this.yearField.getValue();
30236     },
30237     
30238     getValue : function()
30239     {
30240         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30241         
30242         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30243         
30244         return date;
30245     },
30246     
30247     reset : function()
30248     {
30249         this.setDay('');
30250         this.setMonth('');
30251         this.setYear('');
30252         this.inputEl.dom.value = '';
30253         this.validate();
30254         return;
30255     },
30256     
30257     validate : function()
30258     {
30259         var d = this.dayField.validate();
30260         var m = this.monthField.validate();
30261         var y = this.yearField.validate();
30262         
30263         var valid = true;
30264         
30265         if(
30266                 (!this.dayAllowBlank && !d) ||
30267                 (!this.monthAllowBlank && !m) ||
30268                 (!this.yearAllowBlank && !y)
30269         ){
30270             valid = false;
30271         }
30272         
30273         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30274             return valid;
30275         }
30276         
30277         if(valid){
30278             this.markValid();
30279             return valid;
30280         }
30281         
30282         this.markInvalid();
30283         
30284         return valid;
30285     },
30286     
30287     markValid : function()
30288     {
30289         
30290         var label = this.el.select('label', true).first();
30291         var icon = this.el.select('i.fa-star', true).first();
30292
30293         if(label && icon){
30294             icon.remove();
30295         }
30296         
30297         this.fireEvent('valid', this);
30298     },
30299     
30300      /**
30301      * Mark this field as invalid
30302      * @param {String} msg The validation message
30303      */
30304     markInvalid : function(msg)
30305     {
30306         
30307         var label = this.el.select('label', true).first();
30308         var icon = this.el.select('i.fa-star', true).first();
30309
30310         if(label && !icon){
30311             this.el.select('.roo-date-split-field-label', true).createChild({
30312                 tag : 'i',
30313                 cls : 'text-danger fa fa-lg fa-star',
30314                 tooltip : 'This field is required',
30315                 style : 'margin-right:5px;'
30316             }, label, true);
30317         }
30318         
30319         this.fireEvent('invalid', this, msg);
30320     },
30321     
30322     clearInvalid : function()
30323     {
30324         var label = this.el.select('label', true).first();
30325         var icon = this.el.select('i.fa-star', true).first();
30326
30327         if(label && icon){
30328             icon.remove();
30329         }
30330         
30331         this.fireEvent('valid', this);
30332     },
30333     
30334     getName: function()
30335     {
30336         return this.name;
30337     }
30338     
30339 });
30340
30341  /**
30342  *
30343  * This is based on 
30344  * http://masonry.desandro.com
30345  *
30346  * The idea is to render all the bricks based on vertical width...
30347  *
30348  * The original code extends 'outlayer' - we might need to use that....
30349  * 
30350  */
30351
30352
30353 /**
30354  * @class Roo.bootstrap.LayoutMasonry
30355  * @extends Roo.bootstrap.Component
30356  * Bootstrap Layout Masonry class
30357  * 
30358  * @constructor
30359  * Create a new Element
30360  * @param {Object} config The config object
30361  */
30362
30363 Roo.bootstrap.LayoutMasonry = function(config){
30364     
30365     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30366     
30367     this.bricks = [];
30368     
30369     Roo.bootstrap.LayoutMasonry.register(this);
30370     
30371     this.addEvents({
30372         // raw events
30373         /**
30374          * @event layout
30375          * Fire after layout the items
30376          * @param {Roo.bootstrap.LayoutMasonry} this
30377          * @param {Roo.EventObject} e
30378          */
30379         "layout" : true
30380     });
30381     
30382 };
30383
30384 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30385     
30386     /**
30387      * @cfg {Boolean} isLayoutInstant = no animation?
30388      */   
30389     isLayoutInstant : false, // needed?
30390    
30391     /**
30392      * @cfg {Number} boxWidth  width of the columns
30393      */   
30394     boxWidth : 450,
30395     
30396       /**
30397      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30398      */   
30399     boxHeight : 0,
30400     
30401     /**
30402      * @cfg {Number} padWidth padding below box..
30403      */   
30404     padWidth : 10, 
30405     
30406     /**
30407      * @cfg {Number} gutter gutter width..
30408      */   
30409     gutter : 10,
30410     
30411      /**
30412      * @cfg {Number} maxCols maximum number of columns
30413      */   
30414     
30415     maxCols: 0,
30416     
30417     /**
30418      * @cfg {Boolean} isAutoInitial defalut true
30419      */   
30420     isAutoInitial : true, 
30421     
30422     containerWidth: 0,
30423     
30424     /**
30425      * @cfg {Boolean} isHorizontal defalut false
30426      */   
30427     isHorizontal : false, 
30428
30429     currentSize : null,
30430     
30431     tag: 'div',
30432     
30433     cls: '',
30434     
30435     bricks: null, //CompositeElement
30436     
30437     cols : 1,
30438     
30439     _isLayoutInited : false,
30440     
30441 //    isAlternative : false, // only use for vertical layout...
30442     
30443     /**
30444      * @cfg {Number} alternativePadWidth padding below box..
30445      */   
30446     alternativePadWidth : 50,
30447     
30448     selectedBrick : [],
30449     
30450     getAutoCreate : function(){
30451         
30452         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30453         
30454         var cfg = {
30455             tag: this.tag,
30456             cls: 'blog-masonary-wrapper ' + this.cls,
30457             cn : {
30458                 cls : 'mas-boxes masonary'
30459             }
30460         };
30461         
30462         return cfg;
30463     },
30464     
30465     getChildContainer: function( )
30466     {
30467         if (this.boxesEl) {
30468             return this.boxesEl;
30469         }
30470         
30471         this.boxesEl = this.el.select('.mas-boxes').first();
30472         
30473         return this.boxesEl;
30474     },
30475     
30476     
30477     initEvents : function()
30478     {
30479         var _this = this;
30480         
30481         if(this.isAutoInitial){
30482             Roo.log('hook children rendered');
30483             this.on('childrenrendered', function() {
30484                 Roo.log('children rendered');
30485                 _this.initial();
30486             } ,this);
30487         }
30488     },
30489     
30490     initial : function()
30491     {
30492         this.selectedBrick = [];
30493         
30494         this.currentSize = this.el.getBox(true);
30495         
30496         Roo.EventManager.onWindowResize(this.resize, this); 
30497
30498         if(!this.isAutoInitial){
30499             this.layout();
30500             return;
30501         }
30502         
30503         this.layout();
30504         
30505         return;
30506         //this.layout.defer(500,this);
30507         
30508     },
30509     
30510     resize : function()
30511     {
30512         var cs = this.el.getBox(true);
30513         
30514         if (
30515                 this.currentSize.width == cs.width && 
30516                 this.currentSize.x == cs.x && 
30517                 this.currentSize.height == cs.height && 
30518                 this.currentSize.y == cs.y 
30519         ) {
30520             Roo.log("no change in with or X or Y");
30521             return;
30522         }
30523         
30524         this.currentSize = cs;
30525         
30526         this.layout();
30527         
30528     },
30529     
30530     layout : function()
30531     {   
30532         this._resetLayout();
30533         
30534         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30535         
30536         this.layoutItems( isInstant );
30537       
30538         this._isLayoutInited = true;
30539         
30540         this.fireEvent('layout', this);
30541         
30542     },
30543     
30544     _resetLayout : function()
30545     {
30546         if(this.isHorizontal){
30547             this.horizontalMeasureColumns();
30548             return;
30549         }
30550         
30551         this.verticalMeasureColumns();
30552         
30553     },
30554     
30555     verticalMeasureColumns : function()
30556     {
30557         this.getContainerWidth();
30558         
30559 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30560 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30561 //            return;
30562 //        }
30563         
30564         var boxWidth = this.boxWidth + this.padWidth;
30565         
30566         if(this.containerWidth < this.boxWidth){
30567             boxWidth = this.containerWidth
30568         }
30569         
30570         var containerWidth = this.containerWidth;
30571         
30572         var cols = Math.floor(containerWidth / boxWidth);
30573         
30574         this.cols = Math.max( cols, 1 );
30575         
30576         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30577         
30578         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30579         
30580         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30581         
30582         this.colWidth = boxWidth + avail - this.padWidth;
30583         
30584         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30585         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30586     },
30587     
30588     horizontalMeasureColumns : function()
30589     {
30590         this.getContainerWidth();
30591         
30592         var boxWidth = this.boxWidth;
30593         
30594         if(this.containerWidth < boxWidth){
30595             boxWidth = this.containerWidth;
30596         }
30597         
30598         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30599         
30600         this.el.setHeight(boxWidth);
30601         
30602     },
30603     
30604     getContainerWidth : function()
30605     {
30606         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30607     },
30608     
30609     layoutItems : function( isInstant )
30610     {
30611         Roo.log(this.bricks);
30612         
30613         var items = Roo.apply([], this.bricks);
30614         
30615         if(this.isHorizontal){
30616             this._horizontalLayoutItems( items , isInstant );
30617             return;
30618         }
30619         
30620 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30621 //            this._verticalAlternativeLayoutItems( items , isInstant );
30622 //            return;
30623 //        }
30624         
30625         this._verticalLayoutItems( items , isInstant );
30626         
30627     },
30628     
30629     _verticalLayoutItems : function ( items , isInstant)
30630     {
30631         if ( !items || !items.length ) {
30632             return;
30633         }
30634         
30635         var standard = [
30636             ['xs', 'xs', 'xs', 'tall'],
30637             ['xs', 'xs', 'tall'],
30638             ['xs', 'xs', 'sm'],
30639             ['xs', 'xs', 'xs'],
30640             ['xs', 'tall'],
30641             ['xs', 'sm'],
30642             ['xs', 'xs'],
30643             ['xs'],
30644             
30645             ['sm', 'xs', 'xs'],
30646             ['sm', 'xs'],
30647             ['sm'],
30648             
30649             ['tall', 'xs', 'xs', 'xs'],
30650             ['tall', 'xs', 'xs'],
30651             ['tall', 'xs'],
30652             ['tall']
30653             
30654         ];
30655         
30656         var queue = [];
30657         
30658         var boxes = [];
30659         
30660         var box = [];
30661         
30662         Roo.each(items, function(item, k){
30663             
30664             switch (item.size) {
30665                 // these layouts take up a full box,
30666                 case 'md' :
30667                 case 'md-left' :
30668                 case 'md-right' :
30669                 case 'wide' :
30670                     
30671                     if(box.length){
30672                         boxes.push(box);
30673                         box = [];
30674                     }
30675                     
30676                     boxes.push([item]);
30677                     
30678                     break;
30679                     
30680                 case 'xs' :
30681                 case 'sm' :
30682                 case 'tall' :
30683                     
30684                     box.push(item);
30685                     
30686                     break;
30687                 default :
30688                     break;
30689                     
30690             }
30691             
30692         }, this);
30693         
30694         if(box.length){
30695             boxes.push(box);
30696             box = [];
30697         }
30698         
30699         var filterPattern = function(box, length)
30700         {
30701             if(!box.length){
30702                 return;
30703             }
30704             
30705             var match = false;
30706             
30707             var pattern = box.slice(0, length);
30708             
30709             var format = [];
30710             
30711             Roo.each(pattern, function(i){
30712                 format.push(i.size);
30713             }, this);
30714             
30715             Roo.each(standard, function(s){
30716                 
30717                 if(String(s) != String(format)){
30718                     return;
30719                 }
30720                 
30721                 match = true;
30722                 return false;
30723                 
30724             }, this);
30725             
30726             if(!match && length == 1){
30727                 return;
30728             }
30729             
30730             if(!match){
30731                 filterPattern(box, length - 1);
30732                 return;
30733             }
30734                 
30735             queue.push(pattern);
30736
30737             box = box.slice(length, box.length);
30738
30739             filterPattern(box, 4);
30740
30741             return;
30742             
30743         }
30744         
30745         Roo.each(boxes, function(box, k){
30746             
30747             if(!box.length){
30748                 return;
30749             }
30750             
30751             if(box.length == 1){
30752                 queue.push(box);
30753                 return;
30754             }
30755             
30756             filterPattern(box, 4);
30757             
30758         }, this);
30759         
30760         this._processVerticalLayoutQueue( queue, isInstant );
30761         
30762     },
30763     
30764 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30765 //    {
30766 //        if ( !items || !items.length ) {
30767 //            return;
30768 //        }
30769 //
30770 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30771 //        
30772 //    },
30773     
30774     _horizontalLayoutItems : function ( items , isInstant)
30775     {
30776         if ( !items || !items.length || items.length < 3) {
30777             return;
30778         }
30779         
30780         items.reverse();
30781         
30782         var eItems = items.slice(0, 3);
30783         
30784         items = items.slice(3, items.length);
30785         
30786         var standard = [
30787             ['xs', 'xs', 'xs', 'wide'],
30788             ['xs', 'xs', 'wide'],
30789             ['xs', 'xs', 'sm'],
30790             ['xs', 'xs', 'xs'],
30791             ['xs', 'wide'],
30792             ['xs', 'sm'],
30793             ['xs', 'xs'],
30794             ['xs'],
30795             
30796             ['sm', 'xs', 'xs'],
30797             ['sm', 'xs'],
30798             ['sm'],
30799             
30800             ['wide', 'xs', 'xs', 'xs'],
30801             ['wide', 'xs', 'xs'],
30802             ['wide', 'xs'],
30803             ['wide'],
30804             
30805             ['wide-thin']
30806         ];
30807         
30808         var queue = [];
30809         
30810         var boxes = [];
30811         
30812         var box = [];
30813         
30814         Roo.each(items, function(item, k){
30815             
30816             switch (item.size) {
30817                 case 'md' :
30818                 case 'md-left' :
30819                 case 'md-right' :
30820                 case 'tall' :
30821                     
30822                     if(box.length){
30823                         boxes.push(box);
30824                         box = [];
30825                     }
30826                     
30827                     boxes.push([item]);
30828                     
30829                     break;
30830                     
30831                 case 'xs' :
30832                 case 'sm' :
30833                 case 'wide' :
30834                 case 'wide-thin' :
30835                     
30836                     box.push(item);
30837                     
30838                     break;
30839                 default :
30840                     break;
30841                     
30842             }
30843             
30844         }, this);
30845         
30846         if(box.length){
30847             boxes.push(box);
30848             box = [];
30849         }
30850         
30851         var filterPattern = function(box, length)
30852         {
30853             if(!box.length){
30854                 return;
30855             }
30856             
30857             var match = false;
30858             
30859             var pattern = box.slice(0, length);
30860             
30861             var format = [];
30862             
30863             Roo.each(pattern, function(i){
30864                 format.push(i.size);
30865             }, this);
30866             
30867             Roo.each(standard, function(s){
30868                 
30869                 if(String(s) != String(format)){
30870                     return;
30871                 }
30872                 
30873                 match = true;
30874                 return false;
30875                 
30876             }, this);
30877             
30878             if(!match && length == 1){
30879                 return;
30880             }
30881             
30882             if(!match){
30883                 filterPattern(box, length - 1);
30884                 return;
30885             }
30886                 
30887             queue.push(pattern);
30888
30889             box = box.slice(length, box.length);
30890
30891             filterPattern(box, 4);
30892
30893             return;
30894             
30895         }
30896         
30897         Roo.each(boxes, function(box, k){
30898             
30899             if(!box.length){
30900                 return;
30901             }
30902             
30903             if(box.length == 1){
30904                 queue.push(box);
30905                 return;
30906             }
30907             
30908             filterPattern(box, 4);
30909             
30910         }, this);
30911         
30912         
30913         var prune = [];
30914         
30915         var pos = this.el.getBox(true);
30916         
30917         var minX = pos.x;
30918         
30919         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30920         
30921         var hit_end = false;
30922         
30923         Roo.each(queue, function(box){
30924             
30925             if(hit_end){
30926                 
30927                 Roo.each(box, function(b){
30928                 
30929                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30930                     b.el.hide();
30931
30932                 }, this);
30933
30934                 return;
30935             }
30936             
30937             var mx = 0;
30938             
30939             Roo.each(box, function(b){
30940                 
30941                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30942                 b.el.show();
30943
30944                 mx = Math.max(mx, b.x);
30945                 
30946             }, this);
30947             
30948             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30949             
30950             if(maxX < minX){
30951                 
30952                 Roo.each(box, function(b){
30953                 
30954                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30955                     b.el.hide();
30956                     
30957                 }, this);
30958                 
30959                 hit_end = true;
30960                 
30961                 return;
30962             }
30963             
30964             prune.push(box);
30965             
30966         }, this);
30967         
30968         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30969     },
30970     
30971     /** Sets position of item in DOM
30972     * @param {Element} item
30973     * @param {Number} x - horizontal position
30974     * @param {Number} y - vertical position
30975     * @param {Boolean} isInstant - disables transitions
30976     */
30977     _processVerticalLayoutQueue : function( queue, isInstant )
30978     {
30979         var pos = this.el.getBox(true);
30980         var x = pos.x;
30981         var y = pos.y;
30982         var maxY = [];
30983         
30984         for (var i = 0; i < this.cols; i++){
30985             maxY[i] = pos.y;
30986         }
30987         
30988         Roo.each(queue, function(box, k){
30989             
30990             var col = k % this.cols;
30991             
30992             Roo.each(box, function(b,kk){
30993                 
30994                 b.el.position('absolute');
30995                 
30996                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30997                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30998                 
30999                 if(b.size == 'md-left' || b.size == 'md-right'){
31000                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31001                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31002                 }
31003                 
31004                 b.el.setWidth(width);
31005                 b.el.setHeight(height);
31006                 // iframe?
31007                 b.el.select('iframe',true).setSize(width,height);
31008                 
31009             }, this);
31010             
31011             for (var i = 0; i < this.cols; i++){
31012                 
31013                 if(maxY[i] < maxY[col]){
31014                     col = i;
31015                     continue;
31016                 }
31017                 
31018                 col = Math.min(col, i);
31019                 
31020             }
31021             
31022             x = pos.x + col * (this.colWidth + this.padWidth);
31023             
31024             y = maxY[col];
31025             
31026             var positions = [];
31027             
31028             switch (box.length){
31029                 case 1 :
31030                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31031                     break;
31032                 case 2 :
31033                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31034                     break;
31035                 case 3 :
31036                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31037                     break;
31038                 case 4 :
31039                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31040                     break;
31041                 default :
31042                     break;
31043             }
31044             
31045             Roo.each(box, function(b,kk){
31046                 
31047                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31048                 
31049                 var sz = b.el.getSize();
31050                 
31051                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31052                 
31053             }, this);
31054             
31055         }, this);
31056         
31057         var mY = 0;
31058         
31059         for (var i = 0; i < this.cols; i++){
31060             mY = Math.max(mY, maxY[i]);
31061         }
31062         
31063         this.el.setHeight(mY - pos.y);
31064         
31065     },
31066     
31067 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31068 //    {
31069 //        var pos = this.el.getBox(true);
31070 //        var x = pos.x;
31071 //        var y = pos.y;
31072 //        var maxX = pos.right;
31073 //        
31074 //        var maxHeight = 0;
31075 //        
31076 //        Roo.each(items, function(item, k){
31077 //            
31078 //            var c = k % 2;
31079 //            
31080 //            item.el.position('absolute');
31081 //                
31082 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31083 //
31084 //            item.el.setWidth(width);
31085 //
31086 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31087 //
31088 //            item.el.setHeight(height);
31089 //            
31090 //            if(c == 0){
31091 //                item.el.setXY([x, y], isInstant ? false : true);
31092 //            } else {
31093 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31094 //            }
31095 //            
31096 //            y = y + height + this.alternativePadWidth;
31097 //            
31098 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31099 //            
31100 //        }, this);
31101 //        
31102 //        this.el.setHeight(maxHeight);
31103 //        
31104 //    },
31105     
31106     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31107     {
31108         var pos = this.el.getBox(true);
31109         
31110         var minX = pos.x;
31111         var minY = pos.y;
31112         
31113         var maxX = pos.right;
31114         
31115         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31116         
31117         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31118         
31119         Roo.each(queue, function(box, k){
31120             
31121             Roo.each(box, function(b, kk){
31122                 
31123                 b.el.position('absolute');
31124                 
31125                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31126                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31127                 
31128                 if(b.size == 'md-left' || b.size == 'md-right'){
31129                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31130                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31131                 }
31132                 
31133                 b.el.setWidth(width);
31134                 b.el.setHeight(height);
31135                 
31136             }, this);
31137             
31138             if(!box.length){
31139                 return;
31140             }
31141             
31142             var positions = [];
31143             
31144             switch (box.length){
31145                 case 1 :
31146                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31147                     break;
31148                 case 2 :
31149                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31150                     break;
31151                 case 3 :
31152                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31153                     break;
31154                 case 4 :
31155                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31156                     break;
31157                 default :
31158                     break;
31159             }
31160             
31161             Roo.each(box, function(b,kk){
31162                 
31163                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31164                 
31165                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31166                 
31167             }, this);
31168             
31169         }, this);
31170         
31171     },
31172     
31173     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31174     {
31175         Roo.each(eItems, function(b,k){
31176             
31177             b.size = (k == 0) ? 'sm' : 'xs';
31178             b.x = (k == 0) ? 2 : 1;
31179             b.y = (k == 0) ? 2 : 1;
31180             
31181             b.el.position('absolute');
31182             
31183             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31184                 
31185             b.el.setWidth(width);
31186             
31187             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31188             
31189             b.el.setHeight(height);
31190             
31191         }, this);
31192
31193         var positions = [];
31194         
31195         positions.push({
31196             x : maxX - this.unitWidth * 2 - this.gutter,
31197             y : minY
31198         });
31199         
31200         positions.push({
31201             x : maxX - this.unitWidth,
31202             y : minY + (this.unitWidth + this.gutter) * 2
31203         });
31204         
31205         positions.push({
31206             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31207             y : minY
31208         });
31209         
31210         Roo.each(eItems, function(b,k){
31211             
31212             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31213
31214         }, this);
31215         
31216     },
31217     
31218     getVerticalOneBoxColPositions : function(x, y, box)
31219     {
31220         var pos = [];
31221         
31222         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31223         
31224         if(box[0].size == 'md-left'){
31225             rand = 0;
31226         }
31227         
31228         if(box[0].size == 'md-right'){
31229             rand = 1;
31230         }
31231         
31232         pos.push({
31233             x : x + (this.unitWidth + this.gutter) * rand,
31234             y : y
31235         });
31236         
31237         return pos;
31238     },
31239     
31240     getVerticalTwoBoxColPositions : function(x, y, box)
31241     {
31242         var pos = [];
31243         
31244         if(box[0].size == 'xs'){
31245             
31246             pos.push({
31247                 x : x,
31248                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31249             });
31250
31251             pos.push({
31252                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31253                 y : y
31254             });
31255             
31256             return pos;
31257             
31258         }
31259         
31260         pos.push({
31261             x : x,
31262             y : y
31263         });
31264
31265         pos.push({
31266             x : x + (this.unitWidth + this.gutter) * 2,
31267             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31268         });
31269         
31270         return pos;
31271         
31272     },
31273     
31274     getVerticalThreeBoxColPositions : function(x, y, box)
31275     {
31276         var pos = [];
31277         
31278         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31279             
31280             pos.push({
31281                 x : x,
31282                 y : y
31283             });
31284
31285             pos.push({
31286                 x : x + (this.unitWidth + this.gutter) * 1,
31287                 y : y
31288             });
31289             
31290             pos.push({
31291                 x : x + (this.unitWidth + this.gutter) * 2,
31292                 y : y
31293             });
31294             
31295             return pos;
31296             
31297         }
31298         
31299         if(box[0].size == 'xs' && box[1].size == 'xs'){
31300             
31301             pos.push({
31302                 x : x,
31303                 y : y
31304             });
31305
31306             pos.push({
31307                 x : x,
31308                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31309             });
31310             
31311             pos.push({
31312                 x : x + (this.unitWidth + this.gutter) * 1,
31313                 y : y
31314             });
31315             
31316             return pos;
31317             
31318         }
31319         
31320         pos.push({
31321             x : x,
31322             y : y
31323         });
31324
31325         pos.push({
31326             x : x + (this.unitWidth + this.gutter) * 2,
31327             y : y
31328         });
31329
31330         pos.push({
31331             x : x + (this.unitWidth + this.gutter) * 2,
31332             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31333         });
31334             
31335         return pos;
31336         
31337     },
31338     
31339     getVerticalFourBoxColPositions : function(x, y, box)
31340     {
31341         var pos = [];
31342         
31343         if(box[0].size == 'xs'){
31344             
31345             pos.push({
31346                 x : x,
31347                 y : y
31348             });
31349
31350             pos.push({
31351                 x : x,
31352                 y : y + (this.unitHeight + this.gutter) * 1
31353             });
31354             
31355             pos.push({
31356                 x : x,
31357                 y : y + (this.unitHeight + this.gutter) * 2
31358             });
31359             
31360             pos.push({
31361                 x : x + (this.unitWidth + this.gutter) * 1,
31362                 y : y
31363             });
31364             
31365             return pos;
31366             
31367         }
31368         
31369         pos.push({
31370             x : x,
31371             y : y
31372         });
31373
31374         pos.push({
31375             x : x + (this.unitWidth + this.gutter) * 2,
31376             y : y
31377         });
31378
31379         pos.push({
31380             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31381             y : y + (this.unitHeight + this.gutter) * 1
31382         });
31383
31384         pos.push({
31385             x : x + (this.unitWidth + this.gutter) * 2,
31386             y : y + (this.unitWidth + this.gutter) * 2
31387         });
31388
31389         return pos;
31390         
31391     },
31392     
31393     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31394     {
31395         var pos = [];
31396         
31397         if(box[0].size == 'md-left'){
31398             pos.push({
31399                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31400                 y : minY
31401             });
31402             
31403             return pos;
31404         }
31405         
31406         if(box[0].size == 'md-right'){
31407             pos.push({
31408                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31409                 y : minY + (this.unitWidth + this.gutter) * 1
31410             });
31411             
31412             return pos;
31413         }
31414         
31415         var rand = Math.floor(Math.random() * (4 - box[0].y));
31416         
31417         pos.push({
31418             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31419             y : minY + (this.unitWidth + this.gutter) * rand
31420         });
31421         
31422         return pos;
31423         
31424     },
31425     
31426     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31427     {
31428         var pos = [];
31429         
31430         if(box[0].size == 'xs'){
31431             
31432             pos.push({
31433                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31434                 y : minY
31435             });
31436
31437             pos.push({
31438                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31439                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31440             });
31441             
31442             return pos;
31443             
31444         }
31445         
31446         pos.push({
31447             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31448             y : minY
31449         });
31450
31451         pos.push({
31452             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31453             y : minY + (this.unitWidth + this.gutter) * 2
31454         });
31455         
31456         return pos;
31457         
31458     },
31459     
31460     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31461     {
31462         var pos = [];
31463         
31464         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31465             
31466             pos.push({
31467                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31468                 y : minY
31469             });
31470
31471             pos.push({
31472                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31473                 y : minY + (this.unitWidth + this.gutter) * 1
31474             });
31475             
31476             pos.push({
31477                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31478                 y : minY + (this.unitWidth + this.gutter) * 2
31479             });
31480             
31481             return pos;
31482             
31483         }
31484         
31485         if(box[0].size == 'xs' && box[1].size == 'xs'){
31486             
31487             pos.push({
31488                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31489                 y : minY
31490             });
31491
31492             pos.push({
31493                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31494                 y : minY
31495             });
31496             
31497             pos.push({
31498                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31499                 y : minY + (this.unitWidth + this.gutter) * 1
31500             });
31501             
31502             return pos;
31503             
31504         }
31505         
31506         pos.push({
31507             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31508             y : minY
31509         });
31510
31511         pos.push({
31512             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31513             y : minY + (this.unitWidth + this.gutter) * 2
31514         });
31515
31516         pos.push({
31517             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31518             y : minY + (this.unitWidth + this.gutter) * 2
31519         });
31520             
31521         return pos;
31522         
31523     },
31524     
31525     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31526     {
31527         var pos = [];
31528         
31529         if(box[0].size == 'xs'){
31530             
31531             pos.push({
31532                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31533                 y : minY
31534             });
31535
31536             pos.push({
31537                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31538                 y : minY
31539             });
31540             
31541             pos.push({
31542                 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),
31543                 y : minY
31544             });
31545             
31546             pos.push({
31547                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31548                 y : minY + (this.unitWidth + this.gutter) * 1
31549             });
31550             
31551             return pos;
31552             
31553         }
31554         
31555         pos.push({
31556             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31557             y : minY
31558         });
31559         
31560         pos.push({
31561             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31562             y : minY + (this.unitWidth + this.gutter) * 2
31563         });
31564         
31565         pos.push({
31566             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31567             y : minY + (this.unitWidth + this.gutter) * 2
31568         });
31569         
31570         pos.push({
31571             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),
31572             y : minY + (this.unitWidth + this.gutter) * 2
31573         });
31574
31575         return pos;
31576         
31577     },
31578     
31579     /**
31580     * remove a Masonry Brick
31581     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31582     */
31583     removeBrick : function(brick_id)
31584     {
31585         if (!brick_id) {
31586             return;
31587         }
31588         
31589         for (var i = 0; i<this.bricks.length; i++) {
31590             if (this.bricks[i].id == brick_id) {
31591                 this.bricks.splice(i,1);
31592                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31593                 this.initial();
31594             }
31595         }
31596     },
31597     
31598     /**
31599     * adds a Masonry Brick
31600     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31601     */
31602     addBrick : function(cfg)
31603     {
31604         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31605         //this.register(cn);
31606         cn.parentId = this.id;
31607         cn.onRender(this.el, null);
31608         return cn;
31609     },
31610     
31611     /**
31612     * register a Masonry Brick
31613     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31614     */
31615     
31616     register : function(brick)
31617     {
31618         this.bricks.push(brick);
31619         brick.masonryId = this.id;
31620     },
31621     
31622     /**
31623     * clear all the Masonry Brick
31624     */
31625     clearAll : function()
31626     {
31627         this.bricks = [];
31628         //this.getChildContainer().dom.innerHTML = "";
31629         this.el.dom.innerHTML = '';
31630     },
31631     
31632     getSelected : function()
31633     {
31634         if (!this.selectedBrick) {
31635             return false;
31636         }
31637         
31638         return this.selectedBrick;
31639     }
31640 });
31641
31642 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31643     
31644     groups: {},
31645      /**
31646     * register a Masonry Layout
31647     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31648     */
31649     
31650     register : function(layout)
31651     {
31652         this.groups[layout.id] = layout;
31653     },
31654     /**
31655     * fetch a  Masonry Layout based on the masonry layout ID
31656     * @param {string} the masonry layout to add
31657     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31658     */
31659     
31660     get: function(layout_id) {
31661         if (typeof(this.groups[layout_id]) == 'undefined') {
31662             return false;
31663         }
31664         return this.groups[layout_id] ;
31665     }
31666     
31667     
31668     
31669 });
31670
31671  
31672
31673  /**
31674  *
31675  * This is based on 
31676  * http://masonry.desandro.com
31677  *
31678  * The idea is to render all the bricks based on vertical width...
31679  *
31680  * The original code extends 'outlayer' - we might need to use that....
31681  * 
31682  */
31683
31684
31685 /**
31686  * @class Roo.bootstrap.LayoutMasonryAuto
31687  * @extends Roo.bootstrap.Component
31688  * Bootstrap Layout Masonry class
31689  * 
31690  * @constructor
31691  * Create a new Element
31692  * @param {Object} config The config object
31693  */
31694
31695 Roo.bootstrap.LayoutMasonryAuto = function(config){
31696     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31697 };
31698
31699 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31700     
31701       /**
31702      * @cfg {Boolean} isFitWidth  - resize the width..
31703      */   
31704     isFitWidth : false,  // options..
31705     /**
31706      * @cfg {Boolean} isOriginLeft = left align?
31707      */   
31708     isOriginLeft : true,
31709     /**
31710      * @cfg {Boolean} isOriginTop = top align?
31711      */   
31712     isOriginTop : false,
31713     /**
31714      * @cfg {Boolean} isLayoutInstant = no animation?
31715      */   
31716     isLayoutInstant : false, // needed?
31717     /**
31718      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31719      */   
31720     isResizingContainer : true,
31721     /**
31722      * @cfg {Number} columnWidth  width of the columns 
31723      */   
31724     
31725     columnWidth : 0,
31726     
31727     /**
31728      * @cfg {Number} maxCols maximum number of columns
31729      */   
31730     
31731     maxCols: 0,
31732     /**
31733      * @cfg {Number} padHeight padding below box..
31734      */   
31735     
31736     padHeight : 10, 
31737     
31738     /**
31739      * @cfg {Boolean} isAutoInitial defalut true
31740      */   
31741     
31742     isAutoInitial : true, 
31743     
31744     // private?
31745     gutter : 0,
31746     
31747     containerWidth: 0,
31748     initialColumnWidth : 0,
31749     currentSize : null,
31750     
31751     colYs : null, // array.
31752     maxY : 0,
31753     padWidth: 10,
31754     
31755     
31756     tag: 'div',
31757     cls: '',
31758     bricks: null, //CompositeElement
31759     cols : 0, // array?
31760     // element : null, // wrapped now this.el
31761     _isLayoutInited : null, 
31762     
31763     
31764     getAutoCreate : function(){
31765         
31766         var cfg = {
31767             tag: this.tag,
31768             cls: 'blog-masonary-wrapper ' + this.cls,
31769             cn : {
31770                 cls : 'mas-boxes masonary'
31771             }
31772         };
31773         
31774         return cfg;
31775     },
31776     
31777     getChildContainer: function( )
31778     {
31779         if (this.boxesEl) {
31780             return this.boxesEl;
31781         }
31782         
31783         this.boxesEl = this.el.select('.mas-boxes').first();
31784         
31785         return this.boxesEl;
31786     },
31787     
31788     
31789     initEvents : function()
31790     {
31791         var _this = this;
31792         
31793         if(this.isAutoInitial){
31794             Roo.log('hook children rendered');
31795             this.on('childrenrendered', function() {
31796                 Roo.log('children rendered');
31797                 _this.initial();
31798             } ,this);
31799         }
31800         
31801     },
31802     
31803     initial : function()
31804     {
31805         this.reloadItems();
31806
31807         this.currentSize = this.el.getBox(true);
31808
31809         /// was window resize... - let's see if this works..
31810         Roo.EventManager.onWindowResize(this.resize, this); 
31811
31812         if(!this.isAutoInitial){
31813             this.layout();
31814             return;
31815         }
31816         
31817         this.layout.defer(500,this);
31818     },
31819     
31820     reloadItems: function()
31821     {
31822         this.bricks = this.el.select('.masonry-brick', true);
31823         
31824         this.bricks.each(function(b) {
31825             //Roo.log(b.getSize());
31826             if (!b.attr('originalwidth')) {
31827                 b.attr('originalwidth',  b.getSize().width);
31828             }
31829             
31830         });
31831         
31832         Roo.log(this.bricks.elements.length);
31833     },
31834     
31835     resize : function()
31836     {
31837         Roo.log('resize');
31838         var cs = this.el.getBox(true);
31839         
31840         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31841             Roo.log("no change in with or X");
31842             return;
31843         }
31844         this.currentSize = cs;
31845         this.layout();
31846     },
31847     
31848     layout : function()
31849     {
31850          Roo.log('layout');
31851         this._resetLayout();
31852         //this._manageStamps();
31853       
31854         // don't animate first layout
31855         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31856         this.layoutItems( isInstant );
31857       
31858         // flag for initalized
31859         this._isLayoutInited = true;
31860     },
31861     
31862     layoutItems : function( isInstant )
31863     {
31864         //var items = this._getItemsForLayout( this.items );
31865         // original code supports filtering layout items.. we just ignore it..
31866         
31867         this._layoutItems( this.bricks , isInstant );
31868       
31869         this._postLayout();
31870     },
31871     _layoutItems : function ( items , isInstant)
31872     {
31873        //this.fireEvent( 'layout', this, items );
31874     
31875
31876         if ( !items || !items.elements.length ) {
31877           // no items, emit event with empty array
31878             return;
31879         }
31880
31881         var queue = [];
31882         items.each(function(item) {
31883             Roo.log("layout item");
31884             Roo.log(item);
31885             // get x/y object from method
31886             var position = this._getItemLayoutPosition( item );
31887             // enqueue
31888             position.item = item;
31889             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31890             queue.push( position );
31891         }, this);
31892       
31893         this._processLayoutQueue( queue );
31894     },
31895     /** Sets position of item in DOM
31896     * @param {Element} item
31897     * @param {Number} x - horizontal position
31898     * @param {Number} y - vertical position
31899     * @param {Boolean} isInstant - disables transitions
31900     */
31901     _processLayoutQueue : function( queue )
31902     {
31903         for ( var i=0, len = queue.length; i < len; i++ ) {
31904             var obj = queue[i];
31905             obj.item.position('absolute');
31906             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31907         }
31908     },
31909       
31910     
31911     /**
31912     * Any logic you want to do after each layout,
31913     * i.e. size the container
31914     */
31915     _postLayout : function()
31916     {
31917         this.resizeContainer();
31918     },
31919     
31920     resizeContainer : function()
31921     {
31922         if ( !this.isResizingContainer ) {
31923             return;
31924         }
31925         var size = this._getContainerSize();
31926         if ( size ) {
31927             this.el.setSize(size.width,size.height);
31928             this.boxesEl.setSize(size.width,size.height);
31929         }
31930     },
31931     
31932     
31933     
31934     _resetLayout : function()
31935     {
31936         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31937         this.colWidth = this.el.getWidth();
31938         //this.gutter = this.el.getWidth(); 
31939         
31940         this.measureColumns();
31941
31942         // reset column Y
31943         var i = this.cols;
31944         this.colYs = [];
31945         while (i--) {
31946             this.colYs.push( 0 );
31947         }
31948     
31949         this.maxY = 0;
31950     },
31951
31952     measureColumns : function()
31953     {
31954         this.getContainerWidth();
31955       // if columnWidth is 0, default to outerWidth of first item
31956         if ( !this.columnWidth ) {
31957             var firstItem = this.bricks.first();
31958             Roo.log(firstItem);
31959             this.columnWidth  = this.containerWidth;
31960             if (firstItem && firstItem.attr('originalwidth') ) {
31961                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31962             }
31963             // columnWidth fall back to item of first element
31964             Roo.log("set column width?");
31965                         this.initialColumnWidth = this.columnWidth  ;
31966
31967             // if first elem has no width, default to size of container
31968             
31969         }
31970         
31971         
31972         if (this.initialColumnWidth) {
31973             this.columnWidth = this.initialColumnWidth;
31974         }
31975         
31976         
31977             
31978         // column width is fixed at the top - however if container width get's smaller we should
31979         // reduce it...
31980         
31981         // this bit calcs how man columns..
31982             
31983         var columnWidth = this.columnWidth += this.gutter;
31984       
31985         // calculate columns
31986         var containerWidth = this.containerWidth + this.gutter;
31987         
31988         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31989         // fix rounding errors, typically with gutters
31990         var excess = columnWidth - containerWidth % columnWidth;
31991         
31992         
31993         // if overshoot is less than a pixel, round up, otherwise floor it
31994         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31995         cols = Math[ mathMethod ]( cols );
31996         this.cols = Math.max( cols, 1 );
31997         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31998         
31999          // padding positioning..
32000         var totalColWidth = this.cols * this.columnWidth;
32001         var padavail = this.containerWidth - totalColWidth;
32002         // so for 2 columns - we need 3 'pads'
32003         
32004         var padNeeded = (1+this.cols) * this.padWidth;
32005         
32006         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32007         
32008         this.columnWidth += padExtra
32009         //this.padWidth = Math.floor(padavail /  ( this.cols));
32010         
32011         // adjust colum width so that padding is fixed??
32012         
32013         // we have 3 columns ... total = width * 3
32014         // we have X left over... that should be used by 
32015         
32016         //if (this.expandC) {
32017             
32018         //}
32019         
32020         
32021         
32022     },
32023     
32024     getContainerWidth : function()
32025     {
32026        /* // container is parent if fit width
32027         var container = this.isFitWidth ? this.element.parentNode : this.element;
32028         // check that this.size and size are there
32029         // IE8 triggers resize on body size change, so they might not be
32030         
32031         var size = getSize( container );  //FIXME
32032         this.containerWidth = size && size.innerWidth; //FIXME
32033         */
32034          
32035         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32036         
32037     },
32038     
32039     _getItemLayoutPosition : function( item )  // what is item?
32040     {
32041         // we resize the item to our columnWidth..
32042       
32043         item.setWidth(this.columnWidth);
32044         item.autoBoxAdjust  = false;
32045         
32046         var sz = item.getSize();
32047  
32048         // how many columns does this brick span
32049         var remainder = this.containerWidth % this.columnWidth;
32050         
32051         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32052         // round if off by 1 pixel, otherwise use ceil
32053         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32054         colSpan = Math.min( colSpan, this.cols );
32055         
32056         // normally this should be '1' as we dont' currently allow multi width columns..
32057         
32058         var colGroup = this._getColGroup( colSpan );
32059         // get the minimum Y value from the columns
32060         var minimumY = Math.min.apply( Math, colGroup );
32061         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32062         
32063         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32064          
32065         // position the brick
32066         var position = {
32067             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32068             y: this.currentSize.y + minimumY + this.padHeight
32069         };
32070         
32071         Roo.log(position);
32072         // apply setHeight to necessary columns
32073         var setHeight = minimumY + sz.height + this.padHeight;
32074         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32075         
32076         var setSpan = this.cols + 1 - colGroup.length;
32077         for ( var i = 0; i < setSpan; i++ ) {
32078           this.colYs[ shortColIndex + i ] = setHeight ;
32079         }
32080       
32081         return position;
32082     },
32083     
32084     /**
32085      * @param {Number} colSpan - number of columns the element spans
32086      * @returns {Array} colGroup
32087      */
32088     _getColGroup : function( colSpan )
32089     {
32090         if ( colSpan < 2 ) {
32091           // if brick spans only one column, use all the column Ys
32092           return this.colYs;
32093         }
32094       
32095         var colGroup = [];
32096         // how many different places could this brick fit horizontally
32097         var groupCount = this.cols + 1 - colSpan;
32098         // for each group potential horizontal position
32099         for ( var i = 0; i < groupCount; i++ ) {
32100           // make an array of colY values for that one group
32101           var groupColYs = this.colYs.slice( i, i + colSpan );
32102           // and get the max value of the array
32103           colGroup[i] = Math.max.apply( Math, groupColYs );
32104         }
32105         return colGroup;
32106     },
32107     /*
32108     _manageStamp : function( stamp )
32109     {
32110         var stampSize =  stamp.getSize();
32111         var offset = stamp.getBox();
32112         // get the columns that this stamp affects
32113         var firstX = this.isOriginLeft ? offset.x : offset.right;
32114         var lastX = firstX + stampSize.width;
32115         var firstCol = Math.floor( firstX / this.columnWidth );
32116         firstCol = Math.max( 0, firstCol );
32117         
32118         var lastCol = Math.floor( lastX / this.columnWidth );
32119         // lastCol should not go over if multiple of columnWidth #425
32120         lastCol -= lastX % this.columnWidth ? 0 : 1;
32121         lastCol = Math.min( this.cols - 1, lastCol );
32122         
32123         // set colYs to bottom of the stamp
32124         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32125             stampSize.height;
32126             
32127         for ( var i = firstCol; i <= lastCol; i++ ) {
32128           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32129         }
32130     },
32131     */
32132     
32133     _getContainerSize : function()
32134     {
32135         this.maxY = Math.max.apply( Math, this.colYs );
32136         var size = {
32137             height: this.maxY
32138         };
32139       
32140         if ( this.isFitWidth ) {
32141             size.width = this._getContainerFitWidth();
32142         }
32143       
32144         return size;
32145     },
32146     
32147     _getContainerFitWidth : function()
32148     {
32149         var unusedCols = 0;
32150         // count unused columns
32151         var i = this.cols;
32152         while ( --i ) {
32153           if ( this.colYs[i] !== 0 ) {
32154             break;
32155           }
32156           unusedCols++;
32157         }
32158         // fit container to columns that have been used
32159         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32160     },
32161     
32162     needsResizeLayout : function()
32163     {
32164         var previousWidth = this.containerWidth;
32165         this.getContainerWidth();
32166         return previousWidth !== this.containerWidth;
32167     }
32168  
32169 });
32170
32171  
32172
32173  /*
32174  * - LGPL
32175  *
32176  * element
32177  * 
32178  */
32179
32180 /**
32181  * @class Roo.bootstrap.MasonryBrick
32182  * @extends Roo.bootstrap.Component
32183  * Bootstrap MasonryBrick class
32184  * 
32185  * @constructor
32186  * Create a new MasonryBrick
32187  * @param {Object} config The config object
32188  */
32189
32190 Roo.bootstrap.MasonryBrick = function(config){
32191     
32192     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32193     
32194     Roo.bootstrap.MasonryBrick.register(this);
32195     
32196     this.addEvents({
32197         // raw events
32198         /**
32199          * @event click
32200          * When a MasonryBrick is clcik
32201          * @param {Roo.bootstrap.MasonryBrick} this
32202          * @param {Roo.EventObject} e
32203          */
32204         "click" : true
32205     });
32206 };
32207
32208 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32209     
32210     /**
32211      * @cfg {String} title
32212      */   
32213     title : '',
32214     /**
32215      * @cfg {String} html
32216      */   
32217     html : '',
32218     /**
32219      * @cfg {String} bgimage
32220      */   
32221     bgimage : '',
32222     /**
32223      * @cfg {String} videourl
32224      */   
32225     videourl : '',
32226     /**
32227      * @cfg {String} cls
32228      */   
32229     cls : '',
32230     /**
32231      * @cfg {String} href
32232      */   
32233     href : '',
32234     /**
32235      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32236      */   
32237     size : 'xs',
32238     
32239     /**
32240      * @cfg {String} placetitle (center|bottom)
32241      */   
32242     placetitle : '',
32243     
32244     /**
32245      * @cfg {Boolean} isFitContainer defalut true
32246      */   
32247     isFitContainer : true, 
32248     
32249     /**
32250      * @cfg {Boolean} preventDefault defalut false
32251      */   
32252     preventDefault : false, 
32253     
32254     /**
32255      * @cfg {Boolean} inverse defalut false
32256      */   
32257     maskInverse : false, 
32258     
32259     getAutoCreate : function()
32260     {
32261         if(!this.isFitContainer){
32262             return this.getSplitAutoCreate();
32263         }
32264         
32265         var cls = 'masonry-brick masonry-brick-full';
32266         
32267         if(this.href.length){
32268             cls += ' masonry-brick-link';
32269         }
32270         
32271         if(this.bgimage.length){
32272             cls += ' masonry-brick-image';
32273         }
32274         
32275         if(this.maskInverse){
32276             cls += ' mask-inverse';
32277         }
32278         
32279         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32280             cls += ' enable-mask';
32281         }
32282         
32283         if(this.size){
32284             cls += ' masonry-' + this.size + '-brick';
32285         }
32286         
32287         if(this.placetitle.length){
32288             
32289             switch (this.placetitle) {
32290                 case 'center' :
32291                     cls += ' masonry-center-title';
32292                     break;
32293                 case 'bottom' :
32294                     cls += ' masonry-bottom-title';
32295                     break;
32296                 default:
32297                     break;
32298             }
32299             
32300         } else {
32301             if(!this.html.length && !this.bgimage.length){
32302                 cls += ' masonry-center-title';
32303             }
32304
32305             if(!this.html.length && this.bgimage.length){
32306                 cls += ' masonry-bottom-title';
32307             }
32308         }
32309         
32310         if(this.cls){
32311             cls += ' ' + this.cls;
32312         }
32313         
32314         var cfg = {
32315             tag: (this.href.length) ? 'a' : 'div',
32316             cls: cls,
32317             cn: [
32318                 {
32319                     tag: 'div',
32320                     cls: 'masonry-brick-mask'
32321                 },
32322                 {
32323                     tag: 'div',
32324                     cls: 'masonry-brick-paragraph',
32325                     cn: []
32326                 }
32327             ]
32328         };
32329         
32330         if(this.href.length){
32331             cfg.href = this.href;
32332         }
32333         
32334         var cn = cfg.cn[1].cn;
32335         
32336         if(this.title.length){
32337             cn.push({
32338                 tag: 'h4',
32339                 cls: 'masonry-brick-title',
32340                 html: this.title
32341             });
32342         }
32343         
32344         if(this.html.length){
32345             cn.push({
32346                 tag: 'p',
32347                 cls: 'masonry-brick-text',
32348                 html: this.html
32349             });
32350         }
32351         
32352         if (!this.title.length && !this.html.length) {
32353             cfg.cn[1].cls += ' hide';
32354         }
32355         
32356         if(this.bgimage.length){
32357             cfg.cn.push({
32358                 tag: 'img',
32359                 cls: 'masonry-brick-image-view',
32360                 src: this.bgimage
32361             });
32362         }
32363         
32364         if(this.videourl.length){
32365             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32366             // youtube support only?
32367             cfg.cn.push({
32368                 tag: 'iframe',
32369                 cls: 'masonry-brick-image-view',
32370                 src: vurl,
32371                 frameborder : 0,
32372                 allowfullscreen : true
32373             });
32374         }
32375         
32376         return cfg;
32377         
32378     },
32379     
32380     getSplitAutoCreate : function()
32381     {
32382         var cls = 'masonry-brick masonry-brick-split';
32383         
32384         if(this.href.length){
32385             cls += ' masonry-brick-link';
32386         }
32387         
32388         if(this.bgimage.length){
32389             cls += ' masonry-brick-image';
32390         }
32391         
32392         if(this.size){
32393             cls += ' masonry-' + this.size + '-brick';
32394         }
32395         
32396         switch (this.placetitle) {
32397             case 'center' :
32398                 cls += ' masonry-center-title';
32399                 break;
32400             case 'bottom' :
32401                 cls += ' masonry-bottom-title';
32402                 break;
32403             default:
32404                 if(!this.bgimage.length){
32405                     cls += ' masonry-center-title';
32406                 }
32407
32408                 if(this.bgimage.length){
32409                     cls += ' masonry-bottom-title';
32410                 }
32411                 break;
32412         }
32413         
32414         if(this.cls){
32415             cls += ' ' + this.cls;
32416         }
32417         
32418         var cfg = {
32419             tag: (this.href.length) ? 'a' : 'div',
32420             cls: cls,
32421             cn: [
32422                 {
32423                     tag: 'div',
32424                     cls: 'masonry-brick-split-head',
32425                     cn: [
32426                         {
32427                             tag: 'div',
32428                             cls: 'masonry-brick-paragraph',
32429                             cn: []
32430                         }
32431                     ]
32432                 },
32433                 {
32434                     tag: 'div',
32435                     cls: 'masonry-brick-split-body',
32436                     cn: []
32437                 }
32438             ]
32439         };
32440         
32441         if(this.href.length){
32442             cfg.href = this.href;
32443         }
32444         
32445         if(this.title.length){
32446             cfg.cn[0].cn[0].cn.push({
32447                 tag: 'h4',
32448                 cls: 'masonry-brick-title',
32449                 html: this.title
32450             });
32451         }
32452         
32453         if(this.html.length){
32454             cfg.cn[1].cn.push({
32455                 tag: 'p',
32456                 cls: 'masonry-brick-text',
32457                 html: this.html
32458             });
32459         }
32460
32461         if(this.bgimage.length){
32462             cfg.cn[0].cn.push({
32463                 tag: 'img',
32464                 cls: 'masonry-brick-image-view',
32465                 src: this.bgimage
32466             });
32467         }
32468         
32469         if(this.videourl.length){
32470             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32471             // youtube support only?
32472             cfg.cn[0].cn.cn.push({
32473                 tag: 'iframe',
32474                 cls: 'masonry-brick-image-view',
32475                 src: vurl,
32476                 frameborder : 0,
32477                 allowfullscreen : true
32478             });
32479         }
32480         
32481         return cfg;
32482     },
32483     
32484     initEvents: function() 
32485     {
32486         switch (this.size) {
32487             case 'xs' :
32488                 this.x = 1;
32489                 this.y = 1;
32490                 break;
32491             case 'sm' :
32492                 this.x = 2;
32493                 this.y = 2;
32494                 break;
32495             case 'md' :
32496             case 'md-left' :
32497             case 'md-right' :
32498                 this.x = 3;
32499                 this.y = 3;
32500                 break;
32501             case 'tall' :
32502                 this.x = 2;
32503                 this.y = 3;
32504                 break;
32505             case 'wide' :
32506                 this.x = 3;
32507                 this.y = 2;
32508                 break;
32509             case 'wide-thin' :
32510                 this.x = 3;
32511                 this.y = 1;
32512                 break;
32513                         
32514             default :
32515                 break;
32516         }
32517         
32518         if(Roo.isTouch){
32519             this.el.on('touchstart', this.onTouchStart, this);
32520             this.el.on('touchmove', this.onTouchMove, this);
32521             this.el.on('touchend', this.onTouchEnd, this);
32522             this.el.on('contextmenu', this.onContextMenu, this);
32523         } else {
32524             this.el.on('mouseenter'  ,this.enter, this);
32525             this.el.on('mouseleave', this.leave, this);
32526             this.el.on('click', this.onClick, this);
32527         }
32528         
32529         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32530             this.parent().bricks.push(this);   
32531         }
32532         
32533     },
32534     
32535     onClick: function(e, el)
32536     {
32537         var time = this.endTimer - this.startTimer;
32538         // Roo.log(e.preventDefault());
32539         if(Roo.isTouch){
32540             if(time > 1000){
32541                 e.preventDefault();
32542                 return;
32543             }
32544         }
32545         
32546         if(!this.preventDefault){
32547             return;
32548         }
32549         
32550         e.preventDefault();
32551         
32552         if (this.activcClass != '') {
32553             this.selectBrick();
32554         }
32555         
32556         this.fireEvent('click', this);
32557     },
32558     
32559     enter: function(e, el)
32560     {
32561         e.preventDefault();
32562         
32563         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32564             return;
32565         }
32566         
32567         if(this.bgimage.length && this.html.length){
32568             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32569         }
32570     },
32571     
32572     leave: function(e, el)
32573     {
32574         e.preventDefault();
32575         
32576         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32577             return;
32578         }
32579         
32580         if(this.bgimage.length && this.html.length){
32581             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32582         }
32583     },
32584     
32585     onTouchStart: function(e, el)
32586     {
32587 //        e.preventDefault();
32588         
32589         this.touchmoved = false;
32590         
32591         if(!this.isFitContainer){
32592             return;
32593         }
32594         
32595         if(!this.bgimage.length || !this.html.length){
32596             return;
32597         }
32598         
32599         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32600         
32601         this.timer = new Date().getTime();
32602         
32603     },
32604     
32605     onTouchMove: function(e, el)
32606     {
32607         this.touchmoved = true;
32608     },
32609     
32610     onContextMenu : function(e,el)
32611     {
32612         e.preventDefault();
32613         e.stopPropagation();
32614         return false;
32615     },
32616     
32617     onTouchEnd: function(e, el)
32618     {
32619 //        e.preventDefault();
32620         
32621         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32622         
32623             this.leave(e,el);
32624             
32625             return;
32626         }
32627         
32628         if(!this.bgimage.length || !this.html.length){
32629             
32630             if(this.href.length){
32631                 window.location.href = this.href;
32632             }
32633             
32634             return;
32635         }
32636         
32637         if(!this.isFitContainer){
32638             return;
32639         }
32640         
32641         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32642         
32643         window.location.href = this.href;
32644     },
32645     
32646     //selection on single brick only
32647     selectBrick : function() {
32648         
32649         if (!this.parentId) {
32650             return;
32651         }
32652         
32653         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32654         var index = m.selectedBrick.indexOf(this.id);
32655         
32656         if ( index > -1) {
32657             m.selectedBrick.splice(index,1);
32658             this.el.removeClass(this.activeClass);
32659             return;
32660         }
32661         
32662         for(var i = 0; i < m.selectedBrick.length; i++) {
32663             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32664             b.el.removeClass(b.activeClass);
32665         }
32666         
32667         m.selectedBrick = [];
32668         
32669         m.selectedBrick.push(this.id);
32670         this.el.addClass(this.activeClass);
32671         return;
32672     }
32673     
32674 });
32675
32676 Roo.apply(Roo.bootstrap.MasonryBrick, {
32677     
32678     //groups: {},
32679     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32680      /**
32681     * register a Masonry Brick
32682     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32683     */
32684     
32685     register : function(brick)
32686     {
32687         //this.groups[brick.id] = brick;
32688         this.groups.add(brick.id, brick);
32689     },
32690     /**
32691     * fetch a  masonry brick based on the masonry brick ID
32692     * @param {string} the masonry brick to add
32693     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32694     */
32695     
32696     get: function(brick_id) 
32697     {
32698         // if (typeof(this.groups[brick_id]) == 'undefined') {
32699         //     return false;
32700         // }
32701         // return this.groups[brick_id] ;
32702         
32703         if(this.groups.key(brick_id)) {
32704             return this.groups.key(brick_id);
32705         }
32706         
32707         return false;
32708     }
32709     
32710     
32711     
32712 });
32713
32714  /*
32715  * - LGPL
32716  *
32717  * element
32718  * 
32719  */
32720
32721 /**
32722  * @class Roo.bootstrap.Brick
32723  * @extends Roo.bootstrap.Component
32724  * Bootstrap Brick class
32725  * 
32726  * @constructor
32727  * Create a new Brick
32728  * @param {Object} config The config object
32729  */
32730
32731 Roo.bootstrap.Brick = function(config){
32732     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32733     
32734     this.addEvents({
32735         // raw events
32736         /**
32737          * @event click
32738          * When a Brick is click
32739          * @param {Roo.bootstrap.Brick} this
32740          * @param {Roo.EventObject} e
32741          */
32742         "click" : true
32743     });
32744 };
32745
32746 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32747     
32748     /**
32749      * @cfg {String} title
32750      */   
32751     title : '',
32752     /**
32753      * @cfg {String} html
32754      */   
32755     html : '',
32756     /**
32757      * @cfg {String} bgimage
32758      */   
32759     bgimage : '',
32760     /**
32761      * @cfg {String} cls
32762      */   
32763     cls : '',
32764     /**
32765      * @cfg {String} href
32766      */   
32767     href : '',
32768     /**
32769      * @cfg {String} video
32770      */   
32771     video : '',
32772     /**
32773      * @cfg {Boolean} square
32774      */   
32775     square : true,
32776     
32777     getAutoCreate : function()
32778     {
32779         var cls = 'roo-brick';
32780         
32781         if(this.href.length){
32782             cls += ' roo-brick-link';
32783         }
32784         
32785         if(this.bgimage.length){
32786             cls += ' roo-brick-image';
32787         }
32788         
32789         if(!this.html.length && !this.bgimage.length){
32790             cls += ' roo-brick-center-title';
32791         }
32792         
32793         if(!this.html.length && this.bgimage.length){
32794             cls += ' roo-brick-bottom-title';
32795         }
32796         
32797         if(this.cls){
32798             cls += ' ' + this.cls;
32799         }
32800         
32801         var cfg = {
32802             tag: (this.href.length) ? 'a' : 'div',
32803             cls: cls,
32804             cn: [
32805                 {
32806                     tag: 'div',
32807                     cls: 'roo-brick-paragraph',
32808                     cn: []
32809                 }
32810             ]
32811         };
32812         
32813         if(this.href.length){
32814             cfg.href = this.href;
32815         }
32816         
32817         var cn = cfg.cn[0].cn;
32818         
32819         if(this.title.length){
32820             cn.push({
32821                 tag: 'h4',
32822                 cls: 'roo-brick-title',
32823                 html: this.title
32824             });
32825         }
32826         
32827         if(this.html.length){
32828             cn.push({
32829                 tag: 'p',
32830                 cls: 'roo-brick-text',
32831                 html: this.html
32832             });
32833         } else {
32834             cn.cls += ' hide';
32835         }
32836         
32837         if(this.bgimage.length){
32838             cfg.cn.push({
32839                 tag: 'img',
32840                 cls: 'roo-brick-image-view',
32841                 src: this.bgimage
32842             });
32843         }
32844         
32845         return cfg;
32846     },
32847     
32848     initEvents: function() 
32849     {
32850         if(this.title.length || this.html.length){
32851             this.el.on('mouseenter'  ,this.enter, this);
32852             this.el.on('mouseleave', this.leave, this);
32853         }
32854         
32855         Roo.EventManager.onWindowResize(this.resize, this); 
32856         
32857         if(this.bgimage.length){
32858             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32859             this.imageEl.on('load', this.onImageLoad, this);
32860             return;
32861         }
32862         
32863         this.resize();
32864     },
32865     
32866     onImageLoad : function()
32867     {
32868         this.resize();
32869     },
32870     
32871     resize : function()
32872     {
32873         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32874         
32875         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32876         
32877         if(this.bgimage.length){
32878             var image = this.el.select('.roo-brick-image-view', true).first();
32879             
32880             image.setWidth(paragraph.getWidth());
32881             
32882             if(this.square){
32883                 image.setHeight(paragraph.getWidth());
32884             }
32885             
32886             this.el.setHeight(image.getHeight());
32887             paragraph.setHeight(image.getHeight());
32888             
32889         }
32890         
32891     },
32892     
32893     enter: function(e, el)
32894     {
32895         e.preventDefault();
32896         
32897         if(this.bgimage.length){
32898             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32899             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32900         }
32901     },
32902     
32903     leave: function(e, el)
32904     {
32905         e.preventDefault();
32906         
32907         if(this.bgimage.length){
32908             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32909             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32910         }
32911     }
32912     
32913 });
32914
32915  
32916
32917  /*
32918  * - LGPL
32919  *
32920  * Input
32921  * 
32922  */
32923
32924 /**
32925  * @class Roo.bootstrap.NumberField
32926  * @extends Roo.bootstrap.Input
32927  * Bootstrap NumberField class
32928  * 
32929  * 
32930  * 
32931  * 
32932  * @constructor
32933  * Create a new NumberField
32934  * @param {Object} config The config object
32935  */
32936
32937 Roo.bootstrap.NumberField = function(config){
32938     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32939 };
32940
32941 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32942     
32943     /**
32944      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32945      */
32946     allowDecimals : true,
32947     /**
32948      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32949      */
32950     decimalSeparator : ".",
32951     /**
32952      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32953      */
32954     decimalPrecision : 2,
32955     /**
32956      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32957      */
32958     allowNegative : true,
32959     /**
32960      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32961      */
32962     minValue : Number.NEGATIVE_INFINITY,
32963     /**
32964      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32965      */
32966     maxValue : Number.MAX_VALUE,
32967     /**
32968      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32969      */
32970     minText : "The minimum value for this field is {0}",
32971     /**
32972      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32973      */
32974     maxText : "The maximum value for this field is {0}",
32975     /**
32976      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32977      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32978      */
32979     nanText : "{0} is not a valid number",
32980     /**
32981      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32982      */
32983     castInt : true,
32984
32985     // private
32986     initEvents : function()
32987     {   
32988         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32989         
32990         var allowed = "0123456789";
32991         
32992         if(this.allowDecimals){
32993             allowed += this.decimalSeparator;
32994         }
32995         
32996         if(this.allowNegative){
32997             allowed += "-";
32998         }
32999         
33000         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33001         
33002         var keyPress = function(e){
33003             
33004             var k = e.getKey();
33005             
33006             var c = e.getCharCode();
33007             
33008             if(
33009                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33010                     allowed.indexOf(String.fromCharCode(c)) === -1
33011             ){
33012                 e.stopEvent();
33013                 return;
33014             }
33015             
33016             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33017                 return;
33018             }
33019             
33020             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33021                 e.stopEvent();
33022             }
33023         };
33024         
33025         this.el.on("keypress", keyPress, this);
33026     },
33027     
33028     validateValue : function(value)
33029     {
33030         
33031         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33032             return false;
33033         }
33034         
33035         var num = this.parseValue(value);
33036         
33037         if(isNaN(num)){
33038             this.markInvalid(String.format(this.nanText, value));
33039             return false;
33040         }
33041         
33042         if(num < this.minValue){
33043             this.markInvalid(String.format(this.minText, this.minValue));
33044             return false;
33045         }
33046         
33047         if(num > this.maxValue){
33048             this.markInvalid(String.format(this.maxText, this.maxValue));
33049             return false;
33050         }
33051         
33052         return true;
33053     },
33054
33055     getValue : function()
33056     {
33057         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33058     },
33059
33060     parseValue : function(value)
33061     {
33062         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33063         return isNaN(value) ? '' : value;
33064     },
33065
33066     fixPrecision : function(value)
33067     {
33068         var nan = isNaN(value);
33069         
33070         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33071             return nan ? '' : value;
33072         }
33073         return parseFloat(value).toFixed(this.decimalPrecision);
33074     },
33075
33076     setValue : function(v)
33077     {
33078         v = this.fixPrecision(v);
33079         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33080     },
33081
33082     decimalPrecisionFcn : function(v)
33083     {
33084         return Math.floor(v);
33085     },
33086
33087     beforeBlur : function()
33088     {
33089         if(!this.castInt){
33090             return;
33091         }
33092         
33093         var v = this.parseValue(this.getRawValue());
33094         if(v){
33095             this.setValue(v);
33096         }
33097     }
33098     
33099 });
33100
33101  
33102
33103 /*
33104 * Licence: LGPL
33105 */
33106
33107 /**
33108  * @class Roo.bootstrap.DocumentSlider
33109  * @extends Roo.bootstrap.Component
33110  * Bootstrap DocumentSlider class
33111  * 
33112  * @constructor
33113  * Create a new DocumentViewer
33114  * @param {Object} config The config object
33115  */
33116
33117 Roo.bootstrap.DocumentSlider = function(config){
33118     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33119     
33120     this.files = [];
33121     
33122     this.addEvents({
33123         /**
33124          * @event initial
33125          * Fire after initEvent
33126          * @param {Roo.bootstrap.DocumentSlider} this
33127          */
33128         "initial" : true,
33129         /**
33130          * @event update
33131          * Fire after update
33132          * @param {Roo.bootstrap.DocumentSlider} this
33133          */
33134         "update" : true,
33135         /**
33136          * @event click
33137          * Fire after click
33138          * @param {Roo.bootstrap.DocumentSlider} this
33139          */
33140         "click" : true
33141     });
33142 };
33143
33144 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33145     
33146     files : false,
33147     
33148     indicator : 0,
33149     
33150     getAutoCreate : function()
33151     {
33152         var cfg = {
33153             tag : 'div',
33154             cls : 'roo-document-slider',
33155             cn : [
33156                 {
33157                     tag : 'div',
33158                     cls : 'roo-document-slider-header',
33159                     cn : [
33160                         {
33161                             tag : 'div',
33162                             cls : 'roo-document-slider-header-title'
33163                         }
33164                     ]
33165                 },
33166                 {
33167                     tag : 'div',
33168                     cls : 'roo-document-slider-body',
33169                     cn : [
33170                         {
33171                             tag : 'div',
33172                             cls : 'roo-document-slider-prev',
33173                             cn : [
33174                                 {
33175                                     tag : 'i',
33176                                     cls : 'fa fa-chevron-left'
33177                                 }
33178                             ]
33179                         },
33180                         {
33181                             tag : 'div',
33182                             cls : 'roo-document-slider-thumb',
33183                             cn : [
33184                                 {
33185                                     tag : 'img',
33186                                     cls : 'roo-document-slider-image'
33187                                 }
33188                             ]
33189                         },
33190                         {
33191                             tag : 'div',
33192                             cls : 'roo-document-slider-next',
33193                             cn : [
33194                                 {
33195                                     tag : 'i',
33196                                     cls : 'fa fa-chevron-right'
33197                                 }
33198                             ]
33199                         }
33200                     ]
33201                 }
33202             ]
33203         };
33204         
33205         return cfg;
33206     },
33207     
33208     initEvents : function()
33209     {
33210         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33211         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33212         
33213         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33214         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33215         
33216         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33217         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33218         
33219         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33220         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33221         
33222         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33223         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33224         
33225         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33226         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33227         
33228         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33229         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33230         
33231         this.thumbEl.on('click', this.onClick, this);
33232         
33233         this.prevIndicator.on('click', this.prev, this);
33234         
33235         this.nextIndicator.on('click', this.next, this);
33236         
33237     },
33238     
33239     initial : function()
33240     {
33241         if(this.files.length){
33242             this.indicator = 1;
33243             this.update()
33244         }
33245         
33246         this.fireEvent('initial', this);
33247     },
33248     
33249     update : function()
33250     {
33251         this.imageEl.attr('src', this.files[this.indicator - 1]);
33252         
33253         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33254         
33255         this.prevIndicator.show();
33256         
33257         if(this.indicator == 1){
33258             this.prevIndicator.hide();
33259         }
33260         
33261         this.nextIndicator.show();
33262         
33263         if(this.indicator == this.files.length){
33264             this.nextIndicator.hide();
33265         }
33266         
33267         this.thumbEl.scrollTo('top');
33268         
33269         this.fireEvent('update', this);
33270     },
33271     
33272     onClick : function(e)
33273     {
33274         e.preventDefault();
33275         
33276         this.fireEvent('click', this);
33277     },
33278     
33279     prev : function(e)
33280     {
33281         e.preventDefault();
33282         
33283         this.indicator = Math.max(1, this.indicator - 1);
33284         
33285         this.update();
33286     },
33287     
33288     next : function(e)
33289     {
33290         e.preventDefault();
33291         
33292         this.indicator = Math.min(this.files.length, this.indicator + 1);
33293         
33294         this.update();
33295     }
33296 });
33297 /*
33298  * - LGPL
33299  *
33300  * RadioSet
33301  *
33302  *
33303  */
33304
33305 /**
33306  * @class Roo.bootstrap.RadioSet
33307  * @extends Roo.bootstrap.Input
33308  * Bootstrap RadioSet class
33309  * @cfg {String} indicatorpos (left|right) default left
33310  * @cfg {Boolean} inline (true|false) inline the element (default true)
33311  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33312  * @constructor
33313  * Create a new RadioSet
33314  * @param {Object} config The config object
33315  */
33316
33317 Roo.bootstrap.RadioSet = function(config){
33318     
33319     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33320     
33321     this.radioes = [];
33322     
33323     Roo.bootstrap.RadioSet.register(this);
33324     
33325     this.addEvents({
33326         /**
33327         * @event check
33328         * Fires when the element is checked or unchecked.
33329         * @param {Roo.bootstrap.RadioSet} this This radio
33330         * @param {Roo.bootstrap.Radio} item The checked item
33331         */
33332        check : true
33333     });
33334     
33335 };
33336
33337 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33338
33339     radioes : false,
33340     
33341     inline : true,
33342     
33343     weight : '',
33344     
33345     indicatorpos : 'left',
33346     
33347     getAutoCreate : function()
33348     {
33349         var label = {
33350             tag : 'label',
33351             cls : 'roo-radio-set-label',
33352             cn : [
33353                 {
33354                     tag : 'span',
33355                     html : this.fieldLabel
33356                 }
33357             ]
33358         };
33359         
33360         if(this.indicatorpos == 'left'){
33361             label.cn.unshift({
33362                 tag : 'i',
33363                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33364                 tooltip : 'This field is required'
33365             });
33366         } else {
33367             label.cn.push({
33368                 tag : 'i',
33369                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33370                 tooltip : 'This field is required'
33371             });
33372         }
33373         
33374         var items = {
33375             tag : 'div',
33376             cls : 'roo-radio-set-items'
33377         };
33378         
33379         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33380         
33381         if (align === 'left' && this.fieldLabel.length) {
33382             
33383             items = {
33384                 cls : "roo-radio-set-right", 
33385                 cn: [
33386                     items
33387                 ]
33388             };
33389             
33390             if(this.labelWidth > 12){
33391                 label.style = "width: " + this.labelWidth + 'px';
33392             }
33393             
33394             if(this.labelWidth < 13 && this.labelmd == 0){
33395                 this.labelmd = this.labelWidth;
33396             }
33397             
33398             if(this.labellg > 0){
33399                 label.cls += ' col-lg-' + this.labellg;
33400                 items.cls += ' col-lg-' + (12 - this.labellg);
33401             }
33402             
33403             if(this.labelmd > 0){
33404                 label.cls += ' col-md-' + this.labelmd;
33405                 items.cls += ' col-md-' + (12 - this.labelmd);
33406             }
33407             
33408             if(this.labelsm > 0){
33409                 label.cls += ' col-sm-' + this.labelsm;
33410                 items.cls += ' col-sm-' + (12 - this.labelsm);
33411             }
33412             
33413             if(this.labelxs > 0){
33414                 label.cls += ' col-xs-' + this.labelxs;
33415                 items.cls += ' col-xs-' + (12 - this.labelxs);
33416             }
33417         }
33418         
33419         var cfg = {
33420             tag : 'div',
33421             cls : 'roo-radio-set',
33422             cn : [
33423                 {
33424                     tag : 'input',
33425                     cls : 'roo-radio-set-input',
33426                     type : 'hidden',
33427                     name : this.name,
33428                     value : this.value ? this.value :  ''
33429                 },
33430                 label,
33431                 items
33432             ]
33433         };
33434         
33435         if(this.weight.length){
33436             cfg.cls += ' roo-radio-' + this.weight;
33437         }
33438         
33439         if(this.inline) {
33440             cfg.cls += ' roo-radio-set-inline';
33441         }
33442         
33443         var settings=this;
33444         ['xs','sm','md','lg'].map(function(size){
33445             if (settings[size]) {
33446                 cfg.cls += ' col-' + size + '-' + settings[size];
33447             }
33448         });
33449         
33450         return cfg;
33451         
33452     },
33453
33454     initEvents : function()
33455     {
33456         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33457         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33458         
33459         if(!this.fieldLabel.length){
33460             this.labelEl.hide();
33461         }
33462         
33463         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33464         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33465         
33466         this.indicatorEl().addClass('invisible');
33467         
33468         this.originalValue = this.getValue();
33469         
33470     },
33471     
33472     inputEl: function ()
33473     {
33474         return this.el.select('.roo-radio-set-input', true).first();
33475     },
33476     
33477     getChildContainer : function()
33478     {
33479         return this.itemsEl;
33480     },
33481     
33482     register : function(item)
33483     {
33484         this.radioes.push(item);
33485         
33486     },
33487     
33488     validate : function()
33489     {   
33490         var valid = false;
33491         
33492         Roo.each(this.radioes, function(i){
33493             if(!i.checked){
33494                 return;
33495             }
33496             
33497             valid = true;
33498             return false;
33499         });
33500         
33501         if(this.allowBlank) {
33502             return true;
33503         }
33504         
33505         if(this.disabled || valid){
33506             this.markValid();
33507             return true;
33508         }
33509         
33510         this.markInvalid();
33511         return false;
33512         
33513     },
33514     
33515     markValid : function()
33516     {
33517         if(this.labelEl.isVisible(true)){
33518             this.indicatorEl().removeClass('visible');
33519             this.indicatorEl().addClass('invisible');
33520         }
33521         
33522         this.el.removeClass([this.invalidClass, this.validClass]);
33523         this.el.addClass(this.validClass);
33524         
33525         this.fireEvent('valid', this);
33526     },
33527     
33528     markInvalid : function(msg)
33529     {
33530         if(this.allowBlank || this.disabled){
33531             return;
33532         }
33533         
33534         if(this.labelEl.isVisible(true)){
33535             this.indicatorEl().removeClass('invisible');
33536             this.indicatorEl().addClass('visible');
33537         }
33538         
33539         this.el.removeClass([this.invalidClass, this.validClass]);
33540         this.el.addClass(this.invalidClass);
33541         
33542         this.fireEvent('invalid', this, msg);
33543         
33544     },
33545     
33546     setValue : function(v, suppressEvent)
33547     {   
33548         if(this.value === v){
33549             return;
33550         }
33551         
33552         this.value = v;
33553         
33554         if(this.rendered){
33555             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33556         }
33557         
33558         Roo.each(this.radioes, function(i){
33559             
33560             i.checked = false;
33561             i.el.removeClass('checked');
33562             
33563             if(i.value === v || i.value.toString() === v.toString()){
33564                 i.checked = true;
33565                 i.el.addClass('checked');
33566                 
33567                 if(suppressEvent !== true){
33568                     this.fireEvent('check', this, i);
33569                 }
33570             }
33571             
33572         }, this);
33573         
33574         this.validate();
33575     },
33576     
33577     clearInvalid : function(){
33578         
33579         if(!this.el || this.preventMark){
33580             return;
33581         }
33582         
33583         this.el.removeClass([this.invalidClass]);
33584         
33585         this.fireEvent('valid', this);
33586     }
33587     
33588 });
33589
33590 Roo.apply(Roo.bootstrap.RadioSet, {
33591     
33592     groups: {},
33593     
33594     register : function(set)
33595     {
33596         this.groups[set.name] = set;
33597     },
33598     
33599     get: function(name) 
33600     {
33601         if (typeof(this.groups[name]) == 'undefined') {
33602             return false;
33603         }
33604         
33605         return this.groups[name] ;
33606     }
33607     
33608 });
33609 /*
33610  * Based on:
33611  * Ext JS Library 1.1.1
33612  * Copyright(c) 2006-2007, Ext JS, LLC.
33613  *
33614  * Originally Released Under LGPL - original licence link has changed is not relivant.
33615  *
33616  * Fork - LGPL
33617  * <script type="text/javascript">
33618  */
33619
33620
33621 /**
33622  * @class Roo.bootstrap.SplitBar
33623  * @extends Roo.util.Observable
33624  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33625  * <br><br>
33626  * Usage:
33627  * <pre><code>
33628 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33629                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33630 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33631 split.minSize = 100;
33632 split.maxSize = 600;
33633 split.animate = true;
33634 split.on('moved', splitterMoved);
33635 </code></pre>
33636  * @constructor
33637  * Create a new SplitBar
33638  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33639  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33640  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33641  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33642                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33643                         position of the SplitBar).
33644  */
33645 Roo.bootstrap.SplitBar = function(cfg){
33646     
33647     /** @private */
33648     
33649     //{
33650     //  dragElement : elm
33651     //  resizingElement: el,
33652         // optional..
33653     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33654     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33655         // existingProxy ???
33656     //}
33657     
33658     this.el = Roo.get(cfg.dragElement, true);
33659     this.el.dom.unselectable = "on";
33660     /** @private */
33661     this.resizingEl = Roo.get(cfg.resizingElement, true);
33662
33663     /**
33664      * @private
33665      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33666      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33667      * @type Number
33668      */
33669     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33670     
33671     /**
33672      * The minimum size of the resizing element. (Defaults to 0)
33673      * @type Number
33674      */
33675     this.minSize = 0;
33676     
33677     /**
33678      * The maximum size of the resizing element. (Defaults to 2000)
33679      * @type Number
33680      */
33681     this.maxSize = 2000;
33682     
33683     /**
33684      * Whether to animate the transition to the new size
33685      * @type Boolean
33686      */
33687     this.animate = false;
33688     
33689     /**
33690      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33691      * @type Boolean
33692      */
33693     this.useShim = false;
33694     
33695     /** @private */
33696     this.shim = null;
33697     
33698     if(!cfg.existingProxy){
33699         /** @private */
33700         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33701     }else{
33702         this.proxy = Roo.get(cfg.existingProxy).dom;
33703     }
33704     /** @private */
33705     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33706     
33707     /** @private */
33708     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33709     
33710     /** @private */
33711     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33712     
33713     /** @private */
33714     this.dragSpecs = {};
33715     
33716     /**
33717      * @private The adapter to use to positon and resize elements
33718      */
33719     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33720     this.adapter.init(this);
33721     
33722     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33723         /** @private */
33724         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33725         this.el.addClass("roo-splitbar-h");
33726     }else{
33727         /** @private */
33728         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33729         this.el.addClass("roo-splitbar-v");
33730     }
33731     
33732     this.addEvents({
33733         /**
33734          * @event resize
33735          * Fires when the splitter is moved (alias for {@link #event-moved})
33736          * @param {Roo.bootstrap.SplitBar} this
33737          * @param {Number} newSize the new width or height
33738          */
33739         "resize" : true,
33740         /**
33741          * @event moved
33742          * Fires when the splitter is moved
33743          * @param {Roo.bootstrap.SplitBar} this
33744          * @param {Number} newSize the new width or height
33745          */
33746         "moved" : true,
33747         /**
33748          * @event beforeresize
33749          * Fires before the splitter is dragged
33750          * @param {Roo.bootstrap.SplitBar} this
33751          */
33752         "beforeresize" : true,
33753
33754         "beforeapply" : true
33755     });
33756
33757     Roo.util.Observable.call(this);
33758 };
33759
33760 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33761     onStartProxyDrag : function(x, y){
33762         this.fireEvent("beforeresize", this);
33763         if(!this.overlay){
33764             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33765             o.unselectable();
33766             o.enableDisplayMode("block");
33767             // all splitbars share the same overlay
33768             Roo.bootstrap.SplitBar.prototype.overlay = o;
33769         }
33770         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33771         this.overlay.show();
33772         Roo.get(this.proxy).setDisplayed("block");
33773         var size = this.adapter.getElementSize(this);
33774         this.activeMinSize = this.getMinimumSize();;
33775         this.activeMaxSize = this.getMaximumSize();;
33776         var c1 = size - this.activeMinSize;
33777         var c2 = Math.max(this.activeMaxSize - size, 0);
33778         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33779             this.dd.resetConstraints();
33780             this.dd.setXConstraint(
33781                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33782                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33783             );
33784             this.dd.setYConstraint(0, 0);
33785         }else{
33786             this.dd.resetConstraints();
33787             this.dd.setXConstraint(0, 0);
33788             this.dd.setYConstraint(
33789                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33790                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33791             );
33792          }
33793         this.dragSpecs.startSize = size;
33794         this.dragSpecs.startPoint = [x, y];
33795         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33796     },
33797     
33798     /** 
33799      * @private Called after the drag operation by the DDProxy
33800      */
33801     onEndProxyDrag : function(e){
33802         Roo.get(this.proxy).setDisplayed(false);
33803         var endPoint = Roo.lib.Event.getXY(e);
33804         if(this.overlay){
33805             this.overlay.hide();
33806         }
33807         var newSize;
33808         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33809             newSize = this.dragSpecs.startSize + 
33810                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33811                     endPoint[0] - this.dragSpecs.startPoint[0] :
33812                     this.dragSpecs.startPoint[0] - endPoint[0]
33813                 );
33814         }else{
33815             newSize = this.dragSpecs.startSize + 
33816                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33817                     endPoint[1] - this.dragSpecs.startPoint[1] :
33818                     this.dragSpecs.startPoint[1] - endPoint[1]
33819                 );
33820         }
33821         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33822         if(newSize != this.dragSpecs.startSize){
33823             if(this.fireEvent('beforeapply', this, newSize) !== false){
33824                 this.adapter.setElementSize(this, newSize);
33825                 this.fireEvent("moved", this, newSize);
33826                 this.fireEvent("resize", this, newSize);
33827             }
33828         }
33829     },
33830     
33831     /**
33832      * Get the adapter this SplitBar uses
33833      * @return The adapter object
33834      */
33835     getAdapter : function(){
33836         return this.adapter;
33837     },
33838     
33839     /**
33840      * Set the adapter this SplitBar uses
33841      * @param {Object} adapter A SplitBar adapter object
33842      */
33843     setAdapter : function(adapter){
33844         this.adapter = adapter;
33845         this.adapter.init(this);
33846     },
33847     
33848     /**
33849      * Gets the minimum size for the resizing element
33850      * @return {Number} The minimum size
33851      */
33852     getMinimumSize : function(){
33853         return this.minSize;
33854     },
33855     
33856     /**
33857      * Sets the minimum size for the resizing element
33858      * @param {Number} minSize The minimum size
33859      */
33860     setMinimumSize : function(minSize){
33861         this.minSize = minSize;
33862     },
33863     
33864     /**
33865      * Gets the maximum size for the resizing element
33866      * @return {Number} The maximum size
33867      */
33868     getMaximumSize : function(){
33869         return this.maxSize;
33870     },
33871     
33872     /**
33873      * Sets the maximum size for the resizing element
33874      * @param {Number} maxSize The maximum size
33875      */
33876     setMaximumSize : function(maxSize){
33877         this.maxSize = maxSize;
33878     },
33879     
33880     /**
33881      * Sets the initialize size for the resizing element
33882      * @param {Number} size The initial size
33883      */
33884     setCurrentSize : function(size){
33885         var oldAnimate = this.animate;
33886         this.animate = false;
33887         this.adapter.setElementSize(this, size);
33888         this.animate = oldAnimate;
33889     },
33890     
33891     /**
33892      * Destroy this splitbar. 
33893      * @param {Boolean} removeEl True to remove the element
33894      */
33895     destroy : function(removeEl){
33896         if(this.shim){
33897             this.shim.remove();
33898         }
33899         this.dd.unreg();
33900         this.proxy.parentNode.removeChild(this.proxy);
33901         if(removeEl){
33902             this.el.remove();
33903         }
33904     }
33905 });
33906
33907 /**
33908  * @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.
33909  */
33910 Roo.bootstrap.SplitBar.createProxy = function(dir){
33911     var proxy = new Roo.Element(document.createElement("div"));
33912     proxy.unselectable();
33913     var cls = 'roo-splitbar-proxy';
33914     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33915     document.body.appendChild(proxy.dom);
33916     return proxy.dom;
33917 };
33918
33919 /** 
33920  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33921  * Default Adapter. It assumes the splitter and resizing element are not positioned
33922  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33923  */
33924 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33925 };
33926
33927 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33928     // do nothing for now
33929     init : function(s){
33930     
33931     },
33932     /**
33933      * Called before drag operations to get the current size of the resizing element. 
33934      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33935      */
33936      getElementSize : function(s){
33937         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33938             return s.resizingEl.getWidth();
33939         }else{
33940             return s.resizingEl.getHeight();
33941         }
33942     },
33943     
33944     /**
33945      * Called after drag operations to set the size of the resizing element.
33946      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33947      * @param {Number} newSize The new size to set
33948      * @param {Function} onComplete A function to be invoked when resizing is complete
33949      */
33950     setElementSize : function(s, newSize, onComplete){
33951         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33952             if(!s.animate){
33953                 s.resizingEl.setWidth(newSize);
33954                 if(onComplete){
33955                     onComplete(s, newSize);
33956                 }
33957             }else{
33958                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33959             }
33960         }else{
33961             
33962             if(!s.animate){
33963                 s.resizingEl.setHeight(newSize);
33964                 if(onComplete){
33965                     onComplete(s, newSize);
33966                 }
33967             }else{
33968                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33969             }
33970         }
33971     }
33972 };
33973
33974 /** 
33975  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33976  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33977  * Adapter that  moves the splitter element to align with the resized sizing element. 
33978  * Used with an absolute positioned SplitBar.
33979  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33980  * document.body, make sure you assign an id to the body element.
33981  */
33982 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33983     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33984     this.container = Roo.get(container);
33985 };
33986
33987 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33988     init : function(s){
33989         this.basic.init(s);
33990     },
33991     
33992     getElementSize : function(s){
33993         return this.basic.getElementSize(s);
33994     },
33995     
33996     setElementSize : function(s, newSize, onComplete){
33997         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33998     },
33999     
34000     moveSplitter : function(s){
34001         var yes = Roo.bootstrap.SplitBar;
34002         switch(s.placement){
34003             case yes.LEFT:
34004                 s.el.setX(s.resizingEl.getRight());
34005                 break;
34006             case yes.RIGHT:
34007                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34008                 break;
34009             case yes.TOP:
34010                 s.el.setY(s.resizingEl.getBottom());
34011                 break;
34012             case yes.BOTTOM:
34013                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34014                 break;
34015         }
34016     }
34017 };
34018
34019 /**
34020  * Orientation constant - Create a vertical SplitBar
34021  * @static
34022  * @type Number
34023  */
34024 Roo.bootstrap.SplitBar.VERTICAL = 1;
34025
34026 /**
34027  * Orientation constant - Create a horizontal SplitBar
34028  * @static
34029  * @type Number
34030  */
34031 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34032
34033 /**
34034  * Placement constant - The resizing element is to the left of the splitter element
34035  * @static
34036  * @type Number
34037  */
34038 Roo.bootstrap.SplitBar.LEFT = 1;
34039
34040 /**
34041  * Placement constant - The resizing element is to the right of the splitter element
34042  * @static
34043  * @type Number
34044  */
34045 Roo.bootstrap.SplitBar.RIGHT = 2;
34046
34047 /**
34048  * Placement constant - The resizing element is positioned above the splitter element
34049  * @static
34050  * @type Number
34051  */
34052 Roo.bootstrap.SplitBar.TOP = 3;
34053
34054 /**
34055  * Placement constant - The resizing element is positioned under splitter element
34056  * @static
34057  * @type Number
34058  */
34059 Roo.bootstrap.SplitBar.BOTTOM = 4;
34060 Roo.namespace("Roo.bootstrap.layout");/*
34061  * Based on:
34062  * Ext JS Library 1.1.1
34063  * Copyright(c) 2006-2007, Ext JS, LLC.
34064  *
34065  * Originally Released Under LGPL - original licence link has changed is not relivant.
34066  *
34067  * Fork - LGPL
34068  * <script type="text/javascript">
34069  */
34070
34071 /**
34072  * @class Roo.bootstrap.layout.Manager
34073  * @extends Roo.bootstrap.Component
34074  * Base class for layout managers.
34075  */
34076 Roo.bootstrap.layout.Manager = function(config)
34077 {
34078     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34079
34080
34081
34082
34083
34084     /** false to disable window resize monitoring @type Boolean */
34085     this.monitorWindowResize = true;
34086     this.regions = {};
34087     this.addEvents({
34088         /**
34089          * @event layout
34090          * Fires when a layout is performed.
34091          * @param {Roo.LayoutManager} this
34092          */
34093         "layout" : true,
34094         /**
34095          * @event regionresized
34096          * Fires when the user resizes a region.
34097          * @param {Roo.LayoutRegion} region The resized region
34098          * @param {Number} newSize The new size (width for east/west, height for north/south)
34099          */
34100         "regionresized" : true,
34101         /**
34102          * @event regioncollapsed
34103          * Fires when a region is collapsed.
34104          * @param {Roo.LayoutRegion} region The collapsed region
34105          */
34106         "regioncollapsed" : true,
34107         /**
34108          * @event regionexpanded
34109          * Fires when a region is expanded.
34110          * @param {Roo.LayoutRegion} region The expanded region
34111          */
34112         "regionexpanded" : true
34113     });
34114     this.updating = false;
34115
34116     if (config.el) {
34117         this.el = Roo.get(config.el);
34118         this.initEvents();
34119     }
34120
34121 };
34122
34123 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34124
34125
34126     regions : null,
34127
34128     monitorWindowResize : true,
34129
34130
34131     updating : false,
34132
34133
34134     onRender : function(ct, position)
34135     {
34136         if(!this.el){
34137             this.el = Roo.get(ct);
34138             this.initEvents();
34139         }
34140         //this.fireEvent('render',this);
34141     },
34142
34143
34144     initEvents: function()
34145     {
34146
34147
34148         // ie scrollbar fix
34149         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34150             document.body.scroll = "no";
34151         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34152             this.el.position('relative');
34153         }
34154         this.id = this.el.id;
34155         this.el.addClass("roo-layout-container");
34156         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34157         if(this.el.dom != document.body ) {
34158             this.el.on('resize', this.layout,this);
34159             this.el.on('show', this.layout,this);
34160         }
34161
34162     },
34163
34164     /**
34165      * Returns true if this layout is currently being updated
34166      * @return {Boolean}
34167      */
34168     isUpdating : function(){
34169         return this.updating;
34170     },
34171
34172     /**
34173      * Suspend the LayoutManager from doing auto-layouts while
34174      * making multiple add or remove calls
34175      */
34176     beginUpdate : function(){
34177         this.updating = true;
34178     },
34179
34180     /**
34181      * Restore auto-layouts and optionally disable the manager from performing a layout
34182      * @param {Boolean} noLayout true to disable a layout update
34183      */
34184     endUpdate : function(noLayout){
34185         this.updating = false;
34186         if(!noLayout){
34187             this.layout();
34188         }
34189     },
34190
34191     layout: function(){
34192         // abstract...
34193     },
34194
34195     onRegionResized : function(region, newSize){
34196         this.fireEvent("regionresized", region, newSize);
34197         this.layout();
34198     },
34199
34200     onRegionCollapsed : function(region){
34201         this.fireEvent("regioncollapsed", region);
34202     },
34203
34204     onRegionExpanded : function(region){
34205         this.fireEvent("regionexpanded", region);
34206     },
34207
34208     /**
34209      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34210      * performs box-model adjustments.
34211      * @return {Object} The size as an object {width: (the width), height: (the height)}
34212      */
34213     getViewSize : function()
34214     {
34215         var size;
34216         if(this.el.dom != document.body){
34217             size = this.el.getSize();
34218         }else{
34219             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34220         }
34221         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34222         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34223         return size;
34224     },
34225
34226     /**
34227      * Returns the Element this layout is bound to.
34228      * @return {Roo.Element}
34229      */
34230     getEl : function(){
34231         return this.el;
34232     },
34233
34234     /**
34235      * Returns the specified region.
34236      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34237      * @return {Roo.LayoutRegion}
34238      */
34239     getRegion : function(target){
34240         return this.regions[target.toLowerCase()];
34241     },
34242
34243     onWindowResize : function(){
34244         if(this.monitorWindowResize){
34245             this.layout();
34246         }
34247     }
34248 });
34249 /*
34250  * Based on:
34251  * Ext JS Library 1.1.1
34252  * Copyright(c) 2006-2007, Ext JS, LLC.
34253  *
34254  * Originally Released Under LGPL - original licence link has changed is not relivant.
34255  *
34256  * Fork - LGPL
34257  * <script type="text/javascript">
34258  */
34259 /**
34260  * @class Roo.bootstrap.layout.Border
34261  * @extends Roo.bootstrap.layout.Manager
34262  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34263  * please see: examples/bootstrap/nested.html<br><br>
34264  
34265 <b>The container the layout is rendered into can be either the body element or any other element.
34266 If it is not the body element, the container needs to either be an absolute positioned element,
34267 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34268 the container size if it is not the body element.</b>
34269
34270 * @constructor
34271 * Create a new Border
34272 * @param {Object} config Configuration options
34273  */
34274 Roo.bootstrap.layout.Border = function(config){
34275     config = config || {};
34276     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34277     
34278     
34279     
34280     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34281         if(config[region]){
34282             config[region].region = region;
34283             this.addRegion(config[region]);
34284         }
34285     },this);
34286     
34287 };
34288
34289 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34290
34291 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34292     /**
34293      * Creates and adds a new region if it doesn't already exist.
34294      * @param {String} target The target region key (north, south, east, west or center).
34295      * @param {Object} config The regions config object
34296      * @return {BorderLayoutRegion} The new region
34297      */
34298     addRegion : function(config)
34299     {
34300         if(!this.regions[config.region]){
34301             var r = this.factory(config);
34302             this.bindRegion(r);
34303         }
34304         return this.regions[config.region];
34305     },
34306
34307     // private (kinda)
34308     bindRegion : function(r){
34309         this.regions[r.config.region] = r;
34310         
34311         r.on("visibilitychange",    this.layout, this);
34312         r.on("paneladded",          this.layout, this);
34313         r.on("panelremoved",        this.layout, this);
34314         r.on("invalidated",         this.layout, this);
34315         r.on("resized",             this.onRegionResized, this);
34316         r.on("collapsed",           this.onRegionCollapsed, this);
34317         r.on("expanded",            this.onRegionExpanded, this);
34318     },
34319
34320     /**
34321      * Performs a layout update.
34322      */
34323     layout : function()
34324     {
34325         if(this.updating) {
34326             return;
34327         }
34328         
34329         // render all the rebions if they have not been done alreayd?
34330         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34331             if(this.regions[region] && !this.regions[region].bodyEl){
34332                 this.regions[region].onRender(this.el)
34333             }
34334         },this);
34335         
34336         var size = this.getViewSize();
34337         var w = size.width;
34338         var h = size.height;
34339         var centerW = w;
34340         var centerH = h;
34341         var centerY = 0;
34342         var centerX = 0;
34343         //var x = 0, y = 0;
34344
34345         var rs = this.regions;
34346         var north = rs["north"];
34347         var south = rs["south"]; 
34348         var west = rs["west"];
34349         var east = rs["east"];
34350         var center = rs["center"];
34351         //if(this.hideOnLayout){ // not supported anymore
34352             //c.el.setStyle("display", "none");
34353         //}
34354         if(north && north.isVisible()){
34355             var b = north.getBox();
34356             var m = north.getMargins();
34357             b.width = w - (m.left+m.right);
34358             b.x = m.left;
34359             b.y = m.top;
34360             centerY = b.height + b.y + m.bottom;
34361             centerH -= centerY;
34362             north.updateBox(this.safeBox(b));
34363         }
34364         if(south && south.isVisible()){
34365             var b = south.getBox();
34366             var m = south.getMargins();
34367             b.width = w - (m.left+m.right);
34368             b.x = m.left;
34369             var totalHeight = (b.height + m.top + m.bottom);
34370             b.y = h - totalHeight + m.top;
34371             centerH -= totalHeight;
34372             south.updateBox(this.safeBox(b));
34373         }
34374         if(west && west.isVisible()){
34375             var b = west.getBox();
34376             var m = west.getMargins();
34377             b.height = centerH - (m.top+m.bottom);
34378             b.x = m.left;
34379             b.y = centerY + m.top;
34380             var totalWidth = (b.width + m.left + m.right);
34381             centerX += totalWidth;
34382             centerW -= totalWidth;
34383             west.updateBox(this.safeBox(b));
34384         }
34385         if(east && east.isVisible()){
34386             var b = east.getBox();
34387             var m = east.getMargins();
34388             b.height = centerH - (m.top+m.bottom);
34389             var totalWidth = (b.width + m.left + m.right);
34390             b.x = w - totalWidth + m.left;
34391             b.y = centerY + m.top;
34392             centerW -= totalWidth;
34393             east.updateBox(this.safeBox(b));
34394         }
34395         if(center){
34396             var m = center.getMargins();
34397             var centerBox = {
34398                 x: centerX + m.left,
34399                 y: centerY + m.top,
34400                 width: centerW - (m.left+m.right),
34401                 height: centerH - (m.top+m.bottom)
34402             };
34403             //if(this.hideOnLayout){
34404                 //center.el.setStyle("display", "block");
34405             //}
34406             center.updateBox(this.safeBox(centerBox));
34407         }
34408         this.el.repaint();
34409         this.fireEvent("layout", this);
34410     },
34411
34412     // private
34413     safeBox : function(box){
34414         box.width = Math.max(0, box.width);
34415         box.height = Math.max(0, box.height);
34416         return box;
34417     },
34418
34419     /**
34420      * Adds a ContentPanel (or subclass) to this layout.
34421      * @param {String} target The target region key (north, south, east, west or center).
34422      * @param {Roo.ContentPanel} panel The panel to add
34423      * @return {Roo.ContentPanel} The added panel
34424      */
34425     add : function(target, panel){
34426          
34427         target = target.toLowerCase();
34428         return this.regions[target].add(panel);
34429     },
34430
34431     /**
34432      * Remove a ContentPanel (or subclass) to this layout.
34433      * @param {String} target The target region key (north, south, east, west or center).
34434      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34435      * @return {Roo.ContentPanel} The removed panel
34436      */
34437     remove : function(target, panel){
34438         target = target.toLowerCase();
34439         return this.regions[target].remove(panel);
34440     },
34441
34442     /**
34443      * Searches all regions for a panel with the specified id
34444      * @param {String} panelId
34445      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34446      */
34447     findPanel : function(panelId){
34448         var rs = this.regions;
34449         for(var target in rs){
34450             if(typeof rs[target] != "function"){
34451                 var p = rs[target].getPanel(panelId);
34452                 if(p){
34453                     return p;
34454                 }
34455             }
34456         }
34457         return null;
34458     },
34459
34460     /**
34461      * Searches all regions for a panel with the specified id and activates (shows) it.
34462      * @param {String/ContentPanel} panelId The panels id or the panel itself
34463      * @return {Roo.ContentPanel} The shown panel or null
34464      */
34465     showPanel : function(panelId) {
34466       var rs = this.regions;
34467       for(var target in rs){
34468          var r = rs[target];
34469          if(typeof r != "function"){
34470             if(r.hasPanel(panelId)){
34471                return r.showPanel(panelId);
34472             }
34473          }
34474       }
34475       return null;
34476    },
34477
34478    /**
34479      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34480      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34481      */
34482    /*
34483     restoreState : function(provider){
34484         if(!provider){
34485             provider = Roo.state.Manager;
34486         }
34487         var sm = new Roo.LayoutStateManager();
34488         sm.init(this, provider);
34489     },
34490 */
34491  
34492  
34493     /**
34494      * Adds a xtype elements to the layout.
34495      * <pre><code>
34496
34497 layout.addxtype({
34498        xtype : 'ContentPanel',
34499        region: 'west',
34500        items: [ .... ]
34501    }
34502 );
34503
34504 layout.addxtype({
34505         xtype : 'NestedLayoutPanel',
34506         region: 'west',
34507         layout: {
34508            center: { },
34509            west: { }   
34510         },
34511         items : [ ... list of content panels or nested layout panels.. ]
34512    }
34513 );
34514 </code></pre>
34515      * @param {Object} cfg Xtype definition of item to add.
34516      */
34517     addxtype : function(cfg)
34518     {
34519         // basically accepts a pannel...
34520         // can accept a layout region..!?!?
34521         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34522         
34523         
34524         // theory?  children can only be panels??
34525         
34526         //if (!cfg.xtype.match(/Panel$/)) {
34527         //    return false;
34528         //}
34529         var ret = false;
34530         
34531         if (typeof(cfg.region) == 'undefined') {
34532             Roo.log("Failed to add Panel, region was not set");
34533             Roo.log(cfg);
34534             return false;
34535         }
34536         var region = cfg.region;
34537         delete cfg.region;
34538         
34539           
34540         var xitems = [];
34541         if (cfg.items) {
34542             xitems = cfg.items;
34543             delete cfg.items;
34544         }
34545         var nb = false;
34546         
34547         switch(cfg.xtype) 
34548         {
34549             case 'Content':  // ContentPanel (el, cfg)
34550             case 'Scroll':  // ContentPanel (el, cfg)
34551             case 'View': 
34552                 cfg.autoCreate = true;
34553                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34554                 //} else {
34555                 //    var el = this.el.createChild();
34556                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34557                 //}
34558                 
34559                 this.add(region, ret);
34560                 break;
34561             
34562             /*
34563             case 'TreePanel': // our new panel!
34564                 cfg.el = this.el.createChild();
34565                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34566                 this.add(region, ret);
34567                 break;
34568             */
34569             
34570             case 'Nest': 
34571                 // create a new Layout (which is  a Border Layout...
34572                 
34573                 var clayout = cfg.layout;
34574                 clayout.el  = this.el.createChild();
34575                 clayout.items   = clayout.items  || [];
34576                 
34577                 delete cfg.layout;
34578                 
34579                 // replace this exitems with the clayout ones..
34580                 xitems = clayout.items;
34581                  
34582                 // force background off if it's in center...
34583                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34584                     cfg.background = false;
34585                 }
34586                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34587                 
34588                 
34589                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34590                 //console.log('adding nested layout panel '  + cfg.toSource());
34591                 this.add(region, ret);
34592                 nb = {}; /// find first...
34593                 break;
34594             
34595             case 'Grid':
34596                 
34597                 // needs grid and region
34598                 
34599                 //var el = this.getRegion(region).el.createChild();
34600                 /*
34601                  *var el = this.el.createChild();
34602                 // create the grid first...
34603                 cfg.grid.container = el;
34604                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34605                 */
34606                 
34607                 if (region == 'center' && this.active ) {
34608                     cfg.background = false;
34609                 }
34610                 
34611                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34612                 
34613                 this.add(region, ret);
34614                 /*
34615                 if (cfg.background) {
34616                     // render grid on panel activation (if panel background)
34617                     ret.on('activate', function(gp) {
34618                         if (!gp.grid.rendered) {
34619                     //        gp.grid.render(el);
34620                         }
34621                     });
34622                 } else {
34623                   //  cfg.grid.render(el);
34624                 }
34625                 */
34626                 break;
34627            
34628            
34629             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34630                 // it was the old xcomponent building that caused this before.
34631                 // espeically if border is the top element in the tree.
34632                 ret = this;
34633                 break; 
34634                 
34635                     
34636                 
34637                 
34638                 
34639             default:
34640                 /*
34641                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34642                     
34643                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34644                     this.add(region, ret);
34645                 } else {
34646                 */
34647                     Roo.log(cfg);
34648                     throw "Can not add '" + cfg.xtype + "' to Border";
34649                     return null;
34650              
34651                                 
34652              
34653         }
34654         this.beginUpdate();
34655         // add children..
34656         var region = '';
34657         var abn = {};
34658         Roo.each(xitems, function(i)  {
34659             region = nb && i.region ? i.region : false;
34660             
34661             var add = ret.addxtype(i);
34662            
34663             if (region) {
34664                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34665                 if (!i.background) {
34666                     abn[region] = nb[region] ;
34667                 }
34668             }
34669             
34670         });
34671         this.endUpdate();
34672
34673         // make the last non-background panel active..
34674         //if (nb) { Roo.log(abn); }
34675         if (nb) {
34676             
34677             for(var r in abn) {
34678                 region = this.getRegion(r);
34679                 if (region) {
34680                     // tried using nb[r], but it does not work..
34681                      
34682                     region.showPanel(abn[r]);
34683                    
34684                 }
34685             }
34686         }
34687         return ret;
34688         
34689     },
34690     
34691     
34692 // private
34693     factory : function(cfg)
34694     {
34695         
34696         var validRegions = Roo.bootstrap.layout.Border.regions;
34697
34698         var target = cfg.region;
34699         cfg.mgr = this;
34700         
34701         var r = Roo.bootstrap.layout;
34702         Roo.log(target);
34703         switch(target){
34704             case "north":
34705                 return new r.North(cfg);
34706             case "south":
34707                 return new r.South(cfg);
34708             case "east":
34709                 return new r.East(cfg);
34710             case "west":
34711                 return new r.West(cfg);
34712             case "center":
34713                 return new r.Center(cfg);
34714         }
34715         throw 'Layout region "'+target+'" not supported.';
34716     }
34717     
34718     
34719 });
34720  /*
34721  * Based on:
34722  * Ext JS Library 1.1.1
34723  * Copyright(c) 2006-2007, Ext JS, LLC.
34724  *
34725  * Originally Released Under LGPL - original licence link has changed is not relivant.
34726  *
34727  * Fork - LGPL
34728  * <script type="text/javascript">
34729  */
34730  
34731 /**
34732  * @class Roo.bootstrap.layout.Basic
34733  * @extends Roo.util.Observable
34734  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34735  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34736  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34737  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34738  * @cfg {string}   region  the region that it inhabits..
34739  * @cfg {bool}   skipConfig skip config?
34740  * 
34741
34742  */
34743 Roo.bootstrap.layout.Basic = function(config){
34744     
34745     this.mgr = config.mgr;
34746     
34747     this.position = config.region;
34748     
34749     var skipConfig = config.skipConfig;
34750     
34751     this.events = {
34752         /**
34753          * @scope Roo.BasicLayoutRegion
34754          */
34755         
34756         /**
34757          * @event beforeremove
34758          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34759          * @param {Roo.LayoutRegion} this
34760          * @param {Roo.ContentPanel} panel The panel
34761          * @param {Object} e The cancel event object
34762          */
34763         "beforeremove" : true,
34764         /**
34765          * @event invalidated
34766          * Fires when the layout for this region is changed.
34767          * @param {Roo.LayoutRegion} this
34768          */
34769         "invalidated" : true,
34770         /**
34771          * @event visibilitychange
34772          * Fires when this region is shown or hidden 
34773          * @param {Roo.LayoutRegion} this
34774          * @param {Boolean} visibility true or false
34775          */
34776         "visibilitychange" : true,
34777         /**
34778          * @event paneladded
34779          * Fires when a panel is added. 
34780          * @param {Roo.LayoutRegion} this
34781          * @param {Roo.ContentPanel} panel The panel
34782          */
34783         "paneladded" : true,
34784         /**
34785          * @event panelremoved
34786          * Fires when a panel is removed. 
34787          * @param {Roo.LayoutRegion} this
34788          * @param {Roo.ContentPanel} panel The panel
34789          */
34790         "panelremoved" : true,
34791         /**
34792          * @event beforecollapse
34793          * Fires when this region before collapse.
34794          * @param {Roo.LayoutRegion} this
34795          */
34796         "beforecollapse" : true,
34797         /**
34798          * @event collapsed
34799          * Fires when this region is collapsed.
34800          * @param {Roo.LayoutRegion} this
34801          */
34802         "collapsed" : true,
34803         /**
34804          * @event expanded
34805          * Fires when this region is expanded.
34806          * @param {Roo.LayoutRegion} this
34807          */
34808         "expanded" : true,
34809         /**
34810          * @event slideshow
34811          * Fires when this region is slid into view.
34812          * @param {Roo.LayoutRegion} this
34813          */
34814         "slideshow" : true,
34815         /**
34816          * @event slidehide
34817          * Fires when this region slides out of view. 
34818          * @param {Roo.LayoutRegion} this
34819          */
34820         "slidehide" : true,
34821         /**
34822          * @event panelactivated
34823          * Fires when a panel is activated. 
34824          * @param {Roo.LayoutRegion} this
34825          * @param {Roo.ContentPanel} panel The activated panel
34826          */
34827         "panelactivated" : true,
34828         /**
34829          * @event resized
34830          * Fires when the user resizes this region. 
34831          * @param {Roo.LayoutRegion} this
34832          * @param {Number} newSize The new size (width for east/west, height for north/south)
34833          */
34834         "resized" : true
34835     };
34836     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34837     this.panels = new Roo.util.MixedCollection();
34838     this.panels.getKey = this.getPanelId.createDelegate(this);
34839     this.box = null;
34840     this.activePanel = null;
34841     // ensure listeners are added...
34842     
34843     if (config.listeners || config.events) {
34844         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34845             listeners : config.listeners || {},
34846             events : config.events || {}
34847         });
34848     }
34849     
34850     if(skipConfig !== true){
34851         this.applyConfig(config);
34852     }
34853 };
34854
34855 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34856 {
34857     getPanelId : function(p){
34858         return p.getId();
34859     },
34860     
34861     applyConfig : function(config){
34862         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34863         this.config = config;
34864         
34865     },
34866     
34867     /**
34868      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34869      * the width, for horizontal (north, south) the height.
34870      * @param {Number} newSize The new width or height
34871      */
34872     resizeTo : function(newSize){
34873         var el = this.el ? this.el :
34874                  (this.activePanel ? this.activePanel.getEl() : null);
34875         if(el){
34876             switch(this.position){
34877                 case "east":
34878                 case "west":
34879                     el.setWidth(newSize);
34880                     this.fireEvent("resized", this, newSize);
34881                 break;
34882                 case "north":
34883                 case "south":
34884                     el.setHeight(newSize);
34885                     this.fireEvent("resized", this, newSize);
34886                 break;                
34887             }
34888         }
34889     },
34890     
34891     getBox : function(){
34892         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34893     },
34894     
34895     getMargins : function(){
34896         return this.margins;
34897     },
34898     
34899     updateBox : function(box){
34900         this.box = box;
34901         var el = this.activePanel.getEl();
34902         el.dom.style.left = box.x + "px";
34903         el.dom.style.top = box.y + "px";
34904         this.activePanel.setSize(box.width, box.height);
34905     },
34906     
34907     /**
34908      * Returns the container element for this region.
34909      * @return {Roo.Element}
34910      */
34911     getEl : function(){
34912         return this.activePanel;
34913     },
34914     
34915     /**
34916      * Returns true if this region is currently visible.
34917      * @return {Boolean}
34918      */
34919     isVisible : function(){
34920         return this.activePanel ? true : false;
34921     },
34922     
34923     setActivePanel : function(panel){
34924         panel = this.getPanel(panel);
34925         if(this.activePanel && this.activePanel != panel){
34926             this.activePanel.setActiveState(false);
34927             this.activePanel.getEl().setLeftTop(-10000,-10000);
34928         }
34929         this.activePanel = panel;
34930         panel.setActiveState(true);
34931         if(this.box){
34932             panel.setSize(this.box.width, this.box.height);
34933         }
34934         this.fireEvent("panelactivated", this, panel);
34935         this.fireEvent("invalidated");
34936     },
34937     
34938     /**
34939      * Show the specified panel.
34940      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34941      * @return {Roo.ContentPanel} The shown panel or null
34942      */
34943     showPanel : function(panel){
34944         panel = this.getPanel(panel);
34945         if(panel){
34946             this.setActivePanel(panel);
34947         }
34948         return panel;
34949     },
34950     
34951     /**
34952      * Get the active panel for this region.
34953      * @return {Roo.ContentPanel} The active panel or null
34954      */
34955     getActivePanel : function(){
34956         return this.activePanel;
34957     },
34958     
34959     /**
34960      * Add the passed ContentPanel(s)
34961      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34962      * @return {Roo.ContentPanel} The panel added (if only one was added)
34963      */
34964     add : function(panel){
34965         if(arguments.length > 1){
34966             for(var i = 0, len = arguments.length; i < len; i++) {
34967                 this.add(arguments[i]);
34968             }
34969             return null;
34970         }
34971         if(this.hasPanel(panel)){
34972             this.showPanel(panel);
34973             return panel;
34974         }
34975         var el = panel.getEl();
34976         if(el.dom.parentNode != this.mgr.el.dom){
34977             this.mgr.el.dom.appendChild(el.dom);
34978         }
34979         if(panel.setRegion){
34980             panel.setRegion(this);
34981         }
34982         this.panels.add(panel);
34983         el.setStyle("position", "absolute");
34984         if(!panel.background){
34985             this.setActivePanel(panel);
34986             if(this.config.initialSize && this.panels.getCount()==1){
34987                 this.resizeTo(this.config.initialSize);
34988             }
34989         }
34990         this.fireEvent("paneladded", this, panel);
34991         return panel;
34992     },
34993     
34994     /**
34995      * Returns true if the panel is in this region.
34996      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34997      * @return {Boolean}
34998      */
34999     hasPanel : function(panel){
35000         if(typeof panel == "object"){ // must be panel obj
35001             panel = panel.getId();
35002         }
35003         return this.getPanel(panel) ? true : false;
35004     },
35005     
35006     /**
35007      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35008      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35009      * @param {Boolean} preservePanel Overrides the config preservePanel option
35010      * @return {Roo.ContentPanel} The panel that was removed
35011      */
35012     remove : function(panel, preservePanel){
35013         panel = this.getPanel(panel);
35014         if(!panel){
35015             return null;
35016         }
35017         var e = {};
35018         this.fireEvent("beforeremove", this, panel, e);
35019         if(e.cancel === true){
35020             return null;
35021         }
35022         var panelId = panel.getId();
35023         this.panels.removeKey(panelId);
35024         return panel;
35025     },
35026     
35027     /**
35028      * Returns the panel specified or null if it's not in this region.
35029      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35030      * @return {Roo.ContentPanel}
35031      */
35032     getPanel : function(id){
35033         if(typeof id == "object"){ // must be panel obj
35034             return id;
35035         }
35036         return this.panels.get(id);
35037     },
35038     
35039     /**
35040      * Returns this regions position (north/south/east/west/center).
35041      * @return {String} 
35042      */
35043     getPosition: function(){
35044         return this.position;    
35045     }
35046 });/*
35047  * Based on:
35048  * Ext JS Library 1.1.1
35049  * Copyright(c) 2006-2007, Ext JS, LLC.
35050  *
35051  * Originally Released Under LGPL - original licence link has changed is not relivant.
35052  *
35053  * Fork - LGPL
35054  * <script type="text/javascript">
35055  */
35056  
35057 /**
35058  * @class Roo.bootstrap.layout.Region
35059  * @extends Roo.bootstrap.layout.Basic
35060  * This class represents a region in a layout manager.
35061  
35062  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35063  * @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})
35064  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35065  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35066  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35067  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35068  * @cfg {String}    title           The title for the region (overrides panel titles)
35069  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35070  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35071  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35072  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35073  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35074  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35075  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35076  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35077  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35078  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35079
35080  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35081  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35082  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35083  * @cfg {Number}    width           For East/West panels
35084  * @cfg {Number}    height          For North/South panels
35085  * @cfg {Boolean}   split           To show the splitter
35086  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35087  * 
35088  * @cfg {string}   cls             Extra CSS classes to add to region
35089  * 
35090  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35091  * @cfg {string}   region  the region that it inhabits..
35092  *
35093
35094  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35095  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35096
35097  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35098  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35099  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35100  */
35101 Roo.bootstrap.layout.Region = function(config)
35102 {
35103     this.applyConfig(config);
35104
35105     var mgr = config.mgr;
35106     var pos = config.region;
35107     config.skipConfig = true;
35108     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35109     
35110     if (mgr.el) {
35111         this.onRender(mgr.el);   
35112     }
35113      
35114     this.visible = true;
35115     this.collapsed = false;
35116     this.unrendered_panels = [];
35117 };
35118
35119 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35120
35121     position: '', // set by wrapper (eg. north/south etc..)
35122     unrendered_panels : null,  // unrendered panels.
35123     createBody : function(){
35124         /** This region's body element 
35125         * @type Roo.Element */
35126         this.bodyEl = this.el.createChild({
35127                 tag: "div",
35128                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35129         });
35130     },
35131
35132     onRender: function(ctr, pos)
35133     {
35134         var dh = Roo.DomHelper;
35135         /** This region's container element 
35136         * @type Roo.Element */
35137         this.el = dh.append(ctr.dom, {
35138                 tag: "div",
35139                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35140             }, true);
35141         /** This region's title element 
35142         * @type Roo.Element */
35143     
35144         this.titleEl = dh.append(this.el.dom,
35145             {
35146                     tag: "div",
35147                     unselectable: "on",
35148                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35149                     children:[
35150                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35151                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35152                     ]}, true);
35153         
35154         this.titleEl.enableDisplayMode();
35155         /** This region's title text element 
35156         * @type HTMLElement */
35157         this.titleTextEl = this.titleEl.dom.firstChild;
35158         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35159         /*
35160         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35161         this.closeBtn.enableDisplayMode();
35162         this.closeBtn.on("click", this.closeClicked, this);
35163         this.closeBtn.hide();
35164     */
35165         this.createBody(this.config);
35166         if(this.config.hideWhenEmpty){
35167             this.hide();
35168             this.on("paneladded", this.validateVisibility, this);
35169             this.on("panelremoved", this.validateVisibility, this);
35170         }
35171         if(this.autoScroll){
35172             this.bodyEl.setStyle("overflow", "auto");
35173         }else{
35174             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35175         }
35176         //if(c.titlebar !== false){
35177             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35178                 this.titleEl.hide();
35179             }else{
35180                 this.titleEl.show();
35181                 if(this.config.title){
35182                     this.titleTextEl.innerHTML = this.config.title;
35183                 }
35184             }
35185         //}
35186         if(this.config.collapsed){
35187             this.collapse(true);
35188         }
35189         if(this.config.hidden){
35190             this.hide();
35191         }
35192         
35193         if (this.unrendered_panels && this.unrendered_panels.length) {
35194             for (var i =0;i< this.unrendered_panels.length; i++) {
35195                 this.add(this.unrendered_panels[i]);
35196             }
35197             this.unrendered_panels = null;
35198             
35199         }
35200         
35201     },
35202     
35203     applyConfig : function(c)
35204     {
35205         /*
35206          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35207             var dh = Roo.DomHelper;
35208             if(c.titlebar !== false){
35209                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35210                 this.collapseBtn.on("click", this.collapse, this);
35211                 this.collapseBtn.enableDisplayMode();
35212                 /*
35213                 if(c.showPin === true || this.showPin){
35214                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35215                     this.stickBtn.enableDisplayMode();
35216                     this.stickBtn.on("click", this.expand, this);
35217                     this.stickBtn.hide();
35218                 }
35219                 
35220             }
35221             */
35222             /** This region's collapsed element
35223             * @type Roo.Element */
35224             /*
35225              *
35226             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35227                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35228             ]}, true);
35229             
35230             if(c.floatable !== false){
35231                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35232                this.collapsedEl.on("click", this.collapseClick, this);
35233             }
35234
35235             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35236                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35237                    id: "message", unselectable: "on", style:{"float":"left"}});
35238                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35239              }
35240             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35241             this.expandBtn.on("click", this.expand, this);
35242             
35243         }
35244         
35245         if(this.collapseBtn){
35246             this.collapseBtn.setVisible(c.collapsible == true);
35247         }
35248         
35249         this.cmargins = c.cmargins || this.cmargins ||
35250                          (this.position == "west" || this.position == "east" ?
35251                              {top: 0, left: 2, right:2, bottom: 0} :
35252                              {top: 2, left: 0, right:0, bottom: 2});
35253         */
35254         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35255         
35256         
35257         this.bottomTabs = c.tabPosition != "top";
35258         
35259         this.autoScroll = c.autoScroll || false;
35260         
35261         
35262        
35263         
35264         this.duration = c.duration || .30;
35265         this.slideDuration = c.slideDuration || .45;
35266         this.config = c;
35267        
35268     },
35269     /**
35270      * Returns true if this region is currently visible.
35271      * @return {Boolean}
35272      */
35273     isVisible : function(){
35274         return this.visible;
35275     },
35276
35277     /**
35278      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35279      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35280      */
35281     //setCollapsedTitle : function(title){
35282     //    title = title || "&#160;";
35283      //   if(this.collapsedTitleTextEl){
35284       //      this.collapsedTitleTextEl.innerHTML = title;
35285        // }
35286     //},
35287
35288     getBox : function(){
35289         var b;
35290       //  if(!this.collapsed){
35291             b = this.el.getBox(false, true);
35292        // }else{
35293           //  b = this.collapsedEl.getBox(false, true);
35294         //}
35295         return b;
35296     },
35297
35298     getMargins : function(){
35299         return this.margins;
35300         //return this.collapsed ? this.cmargins : this.margins;
35301     },
35302 /*
35303     highlight : function(){
35304         this.el.addClass("x-layout-panel-dragover");
35305     },
35306
35307     unhighlight : function(){
35308         this.el.removeClass("x-layout-panel-dragover");
35309     },
35310 */
35311     updateBox : function(box)
35312     {
35313         if (!this.bodyEl) {
35314             return; // not rendered yet..
35315         }
35316         
35317         this.box = box;
35318         if(!this.collapsed){
35319             this.el.dom.style.left = box.x + "px";
35320             this.el.dom.style.top = box.y + "px";
35321             this.updateBody(box.width, box.height);
35322         }else{
35323             this.collapsedEl.dom.style.left = box.x + "px";
35324             this.collapsedEl.dom.style.top = box.y + "px";
35325             this.collapsedEl.setSize(box.width, box.height);
35326         }
35327         if(this.tabs){
35328             this.tabs.autoSizeTabs();
35329         }
35330     },
35331
35332     updateBody : function(w, h)
35333     {
35334         if(w !== null){
35335             this.el.setWidth(w);
35336             w -= this.el.getBorderWidth("rl");
35337             if(this.config.adjustments){
35338                 w += this.config.adjustments[0];
35339             }
35340         }
35341         if(h !== null && h > 0){
35342             this.el.setHeight(h);
35343             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35344             h -= this.el.getBorderWidth("tb");
35345             if(this.config.adjustments){
35346                 h += this.config.adjustments[1];
35347             }
35348             this.bodyEl.setHeight(h);
35349             if(this.tabs){
35350                 h = this.tabs.syncHeight(h);
35351             }
35352         }
35353         if(this.panelSize){
35354             w = w !== null ? w : this.panelSize.width;
35355             h = h !== null ? h : this.panelSize.height;
35356         }
35357         if(this.activePanel){
35358             var el = this.activePanel.getEl();
35359             w = w !== null ? w : el.getWidth();
35360             h = h !== null ? h : el.getHeight();
35361             this.panelSize = {width: w, height: h};
35362             this.activePanel.setSize(w, h);
35363         }
35364         if(Roo.isIE && this.tabs){
35365             this.tabs.el.repaint();
35366         }
35367     },
35368
35369     /**
35370      * Returns the container element for this region.
35371      * @return {Roo.Element}
35372      */
35373     getEl : function(){
35374         return this.el;
35375     },
35376
35377     /**
35378      * Hides this region.
35379      */
35380     hide : function(){
35381         //if(!this.collapsed){
35382             this.el.dom.style.left = "-2000px";
35383             this.el.hide();
35384         //}else{
35385          //   this.collapsedEl.dom.style.left = "-2000px";
35386          //   this.collapsedEl.hide();
35387        // }
35388         this.visible = false;
35389         this.fireEvent("visibilitychange", this, false);
35390     },
35391
35392     /**
35393      * Shows this region if it was previously hidden.
35394      */
35395     show : function(){
35396         //if(!this.collapsed){
35397             this.el.show();
35398         //}else{
35399         //    this.collapsedEl.show();
35400        // }
35401         this.visible = true;
35402         this.fireEvent("visibilitychange", this, true);
35403     },
35404 /*
35405     closeClicked : function(){
35406         if(this.activePanel){
35407             this.remove(this.activePanel);
35408         }
35409     },
35410
35411     collapseClick : function(e){
35412         if(this.isSlid){
35413            e.stopPropagation();
35414            this.slideIn();
35415         }else{
35416            e.stopPropagation();
35417            this.slideOut();
35418         }
35419     },
35420 */
35421     /**
35422      * Collapses this region.
35423      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35424      */
35425     /*
35426     collapse : function(skipAnim, skipCheck = false){
35427         if(this.collapsed) {
35428             return;
35429         }
35430         
35431         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35432             
35433             this.collapsed = true;
35434             if(this.split){
35435                 this.split.el.hide();
35436             }
35437             if(this.config.animate && skipAnim !== true){
35438                 this.fireEvent("invalidated", this);
35439                 this.animateCollapse();
35440             }else{
35441                 this.el.setLocation(-20000,-20000);
35442                 this.el.hide();
35443                 this.collapsedEl.show();
35444                 this.fireEvent("collapsed", this);
35445                 this.fireEvent("invalidated", this);
35446             }
35447         }
35448         
35449     },
35450 */
35451     animateCollapse : function(){
35452         // overridden
35453     },
35454
35455     /**
35456      * Expands this region if it was previously collapsed.
35457      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35458      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35459      */
35460     /*
35461     expand : function(e, skipAnim){
35462         if(e) {
35463             e.stopPropagation();
35464         }
35465         if(!this.collapsed || this.el.hasActiveFx()) {
35466             return;
35467         }
35468         if(this.isSlid){
35469             this.afterSlideIn();
35470             skipAnim = true;
35471         }
35472         this.collapsed = false;
35473         if(this.config.animate && skipAnim !== true){
35474             this.animateExpand();
35475         }else{
35476             this.el.show();
35477             if(this.split){
35478                 this.split.el.show();
35479             }
35480             this.collapsedEl.setLocation(-2000,-2000);
35481             this.collapsedEl.hide();
35482             this.fireEvent("invalidated", this);
35483             this.fireEvent("expanded", this);
35484         }
35485     },
35486 */
35487     animateExpand : function(){
35488         // overridden
35489     },
35490
35491     initTabs : function()
35492     {
35493         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35494         
35495         var ts = new Roo.bootstrap.panel.Tabs({
35496                 el: this.bodyEl.dom,
35497                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35498                 disableTooltips: this.config.disableTabTips,
35499                 toolbar : this.config.toolbar
35500             });
35501         
35502         if(this.config.hideTabs){
35503             ts.stripWrap.setDisplayed(false);
35504         }
35505         this.tabs = ts;
35506         ts.resizeTabs = this.config.resizeTabs === true;
35507         ts.minTabWidth = this.config.minTabWidth || 40;
35508         ts.maxTabWidth = this.config.maxTabWidth || 250;
35509         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35510         ts.monitorResize = false;
35511         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35512         ts.bodyEl.addClass('roo-layout-tabs-body');
35513         this.panels.each(this.initPanelAsTab, this);
35514     },
35515
35516     initPanelAsTab : function(panel){
35517         var ti = this.tabs.addTab(
35518             panel.getEl().id,
35519             panel.getTitle(),
35520             null,
35521             this.config.closeOnTab && panel.isClosable(),
35522             panel.tpl
35523         );
35524         if(panel.tabTip !== undefined){
35525             ti.setTooltip(panel.tabTip);
35526         }
35527         ti.on("activate", function(){
35528               this.setActivePanel(panel);
35529         }, this);
35530         
35531         if(this.config.closeOnTab){
35532             ti.on("beforeclose", function(t, e){
35533                 e.cancel = true;
35534                 this.remove(panel);
35535             }, this);
35536         }
35537         
35538         panel.tabItem = ti;
35539         
35540         return ti;
35541     },
35542
35543     updatePanelTitle : function(panel, title)
35544     {
35545         if(this.activePanel == panel){
35546             this.updateTitle(title);
35547         }
35548         if(this.tabs){
35549             var ti = this.tabs.getTab(panel.getEl().id);
35550             ti.setText(title);
35551             if(panel.tabTip !== undefined){
35552                 ti.setTooltip(panel.tabTip);
35553             }
35554         }
35555     },
35556
35557     updateTitle : function(title){
35558         if(this.titleTextEl && !this.config.title){
35559             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35560         }
35561     },
35562
35563     setActivePanel : function(panel)
35564     {
35565         panel = this.getPanel(panel);
35566         if(this.activePanel && this.activePanel != panel){
35567             if(this.activePanel.setActiveState(false) === false){
35568                 return;
35569             }
35570         }
35571         this.activePanel = panel;
35572         panel.setActiveState(true);
35573         if(this.panelSize){
35574             panel.setSize(this.panelSize.width, this.panelSize.height);
35575         }
35576         if(this.closeBtn){
35577             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35578         }
35579         this.updateTitle(panel.getTitle());
35580         if(this.tabs){
35581             this.fireEvent("invalidated", this);
35582         }
35583         this.fireEvent("panelactivated", this, panel);
35584     },
35585
35586     /**
35587      * Shows the specified panel.
35588      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35589      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35590      */
35591     showPanel : function(panel)
35592     {
35593         panel = this.getPanel(panel);
35594         if(panel){
35595             if(this.tabs){
35596                 var tab = this.tabs.getTab(panel.getEl().id);
35597                 if(tab.isHidden()){
35598                     this.tabs.unhideTab(tab.id);
35599                 }
35600                 tab.activate();
35601             }else{
35602                 this.setActivePanel(panel);
35603             }
35604         }
35605         return panel;
35606     },
35607
35608     /**
35609      * Get the active panel for this region.
35610      * @return {Roo.ContentPanel} The active panel or null
35611      */
35612     getActivePanel : function(){
35613         return this.activePanel;
35614     },
35615
35616     validateVisibility : function(){
35617         if(this.panels.getCount() < 1){
35618             this.updateTitle("&#160;");
35619             this.closeBtn.hide();
35620             this.hide();
35621         }else{
35622             if(!this.isVisible()){
35623                 this.show();
35624             }
35625         }
35626     },
35627
35628     /**
35629      * Adds the passed ContentPanel(s) to this region.
35630      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35631      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35632      */
35633     add : function(panel)
35634     {
35635         if(arguments.length > 1){
35636             for(var i = 0, len = arguments.length; i < len; i++) {
35637                 this.add(arguments[i]);
35638             }
35639             return null;
35640         }
35641         
35642         // if we have not been rendered yet, then we can not really do much of this..
35643         if (!this.bodyEl) {
35644             this.unrendered_panels.push(panel);
35645             return panel;
35646         }
35647         
35648         
35649         
35650         
35651         if(this.hasPanel(panel)){
35652             this.showPanel(panel);
35653             return panel;
35654         }
35655         panel.setRegion(this);
35656         this.panels.add(panel);
35657        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35658             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35659             // and hide them... ???
35660             this.bodyEl.dom.appendChild(panel.getEl().dom);
35661             if(panel.background !== true){
35662                 this.setActivePanel(panel);
35663             }
35664             this.fireEvent("paneladded", this, panel);
35665             return panel;
35666         }
35667         */
35668         if(!this.tabs){
35669             this.initTabs();
35670         }else{
35671             this.initPanelAsTab(panel);
35672         }
35673         
35674         
35675         if(panel.background !== true){
35676             this.tabs.activate(panel.getEl().id);
35677         }
35678         this.fireEvent("paneladded", this, panel);
35679         return panel;
35680     },
35681
35682     /**
35683      * Hides the tab for the specified panel.
35684      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35685      */
35686     hidePanel : function(panel){
35687         if(this.tabs && (panel = this.getPanel(panel))){
35688             this.tabs.hideTab(panel.getEl().id);
35689         }
35690     },
35691
35692     /**
35693      * Unhides the tab for a previously hidden panel.
35694      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35695      */
35696     unhidePanel : function(panel){
35697         if(this.tabs && (panel = this.getPanel(panel))){
35698             this.tabs.unhideTab(panel.getEl().id);
35699         }
35700     },
35701
35702     clearPanels : function(){
35703         while(this.panels.getCount() > 0){
35704              this.remove(this.panels.first());
35705         }
35706     },
35707
35708     /**
35709      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35710      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35711      * @param {Boolean} preservePanel Overrides the config preservePanel option
35712      * @return {Roo.ContentPanel} The panel that was removed
35713      */
35714     remove : function(panel, preservePanel)
35715     {
35716         panel = this.getPanel(panel);
35717         if(!panel){
35718             return null;
35719         }
35720         var e = {};
35721         this.fireEvent("beforeremove", this, panel, e);
35722         if(e.cancel === true){
35723             return null;
35724         }
35725         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35726         var panelId = panel.getId();
35727         this.panels.removeKey(panelId);
35728         if(preservePanel){
35729             document.body.appendChild(panel.getEl().dom);
35730         }
35731         if(this.tabs){
35732             this.tabs.removeTab(panel.getEl().id);
35733         }else if (!preservePanel){
35734             this.bodyEl.dom.removeChild(panel.getEl().dom);
35735         }
35736         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35737             var p = this.panels.first();
35738             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35739             tempEl.appendChild(p.getEl().dom);
35740             this.bodyEl.update("");
35741             this.bodyEl.dom.appendChild(p.getEl().dom);
35742             tempEl = null;
35743             this.updateTitle(p.getTitle());
35744             this.tabs = null;
35745             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35746             this.setActivePanel(p);
35747         }
35748         panel.setRegion(null);
35749         if(this.activePanel == panel){
35750             this.activePanel = null;
35751         }
35752         if(this.config.autoDestroy !== false && preservePanel !== true){
35753             try{panel.destroy();}catch(e){}
35754         }
35755         this.fireEvent("panelremoved", this, panel);
35756         return panel;
35757     },
35758
35759     /**
35760      * Returns the TabPanel component used by this region
35761      * @return {Roo.TabPanel}
35762      */
35763     getTabs : function(){
35764         return this.tabs;
35765     },
35766
35767     createTool : function(parentEl, className){
35768         var btn = Roo.DomHelper.append(parentEl, {
35769             tag: "div",
35770             cls: "x-layout-tools-button",
35771             children: [ {
35772                 tag: "div",
35773                 cls: "roo-layout-tools-button-inner " + className,
35774                 html: "&#160;"
35775             }]
35776         }, true);
35777         btn.addClassOnOver("roo-layout-tools-button-over");
35778         return btn;
35779     }
35780 });/*
35781  * Based on:
35782  * Ext JS Library 1.1.1
35783  * Copyright(c) 2006-2007, Ext JS, LLC.
35784  *
35785  * Originally Released Under LGPL - original licence link has changed is not relivant.
35786  *
35787  * Fork - LGPL
35788  * <script type="text/javascript">
35789  */
35790  
35791
35792
35793 /**
35794  * @class Roo.SplitLayoutRegion
35795  * @extends Roo.LayoutRegion
35796  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35797  */
35798 Roo.bootstrap.layout.Split = function(config){
35799     this.cursor = config.cursor;
35800     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35801 };
35802
35803 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35804 {
35805     splitTip : "Drag to resize.",
35806     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35807     useSplitTips : false,
35808
35809     applyConfig : function(config){
35810         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35811     },
35812     
35813     onRender : function(ctr,pos) {
35814         
35815         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35816         if(!this.config.split){
35817             return;
35818         }
35819         if(!this.split){
35820             
35821             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35822                             tag: "div",
35823                             id: this.el.id + "-split",
35824                             cls: "roo-layout-split roo-layout-split-"+this.position,
35825                             html: "&#160;"
35826             });
35827             /** The SplitBar for this region 
35828             * @type Roo.SplitBar */
35829             // does not exist yet...
35830             Roo.log([this.position, this.orientation]);
35831             
35832             this.split = new Roo.bootstrap.SplitBar({
35833                 dragElement : splitEl,
35834                 resizingElement: this.el,
35835                 orientation : this.orientation
35836             });
35837             
35838             this.split.on("moved", this.onSplitMove, this);
35839             this.split.useShim = this.config.useShim === true;
35840             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35841             if(this.useSplitTips){
35842                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35843             }
35844             //if(config.collapsible){
35845             //    this.split.el.on("dblclick", this.collapse,  this);
35846             //}
35847         }
35848         if(typeof this.config.minSize != "undefined"){
35849             this.split.minSize = this.config.minSize;
35850         }
35851         if(typeof this.config.maxSize != "undefined"){
35852             this.split.maxSize = this.config.maxSize;
35853         }
35854         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35855             this.hideSplitter();
35856         }
35857         
35858     },
35859
35860     getHMaxSize : function(){
35861          var cmax = this.config.maxSize || 10000;
35862          var center = this.mgr.getRegion("center");
35863          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35864     },
35865
35866     getVMaxSize : function(){
35867          var cmax = this.config.maxSize || 10000;
35868          var center = this.mgr.getRegion("center");
35869          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35870     },
35871
35872     onSplitMove : function(split, newSize){
35873         this.fireEvent("resized", this, newSize);
35874     },
35875     
35876     /** 
35877      * Returns the {@link Roo.SplitBar} for this region.
35878      * @return {Roo.SplitBar}
35879      */
35880     getSplitBar : function(){
35881         return this.split;
35882     },
35883     
35884     hide : function(){
35885         this.hideSplitter();
35886         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35887     },
35888
35889     hideSplitter : function(){
35890         if(this.split){
35891             this.split.el.setLocation(-2000,-2000);
35892             this.split.el.hide();
35893         }
35894     },
35895
35896     show : function(){
35897         if(this.split){
35898             this.split.el.show();
35899         }
35900         Roo.bootstrap.layout.Split.superclass.show.call(this);
35901     },
35902     
35903     beforeSlide: function(){
35904         if(Roo.isGecko){// firefox overflow auto bug workaround
35905             this.bodyEl.clip();
35906             if(this.tabs) {
35907                 this.tabs.bodyEl.clip();
35908             }
35909             if(this.activePanel){
35910                 this.activePanel.getEl().clip();
35911                 
35912                 if(this.activePanel.beforeSlide){
35913                     this.activePanel.beforeSlide();
35914                 }
35915             }
35916         }
35917     },
35918     
35919     afterSlide : function(){
35920         if(Roo.isGecko){// firefox overflow auto bug workaround
35921             this.bodyEl.unclip();
35922             if(this.tabs) {
35923                 this.tabs.bodyEl.unclip();
35924             }
35925             if(this.activePanel){
35926                 this.activePanel.getEl().unclip();
35927                 if(this.activePanel.afterSlide){
35928                     this.activePanel.afterSlide();
35929                 }
35930             }
35931         }
35932     },
35933
35934     initAutoHide : function(){
35935         if(this.autoHide !== false){
35936             if(!this.autoHideHd){
35937                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35938                 this.autoHideHd = {
35939                     "mouseout": function(e){
35940                         if(!e.within(this.el, true)){
35941                             st.delay(500);
35942                         }
35943                     },
35944                     "mouseover" : function(e){
35945                         st.cancel();
35946                     },
35947                     scope : this
35948                 };
35949             }
35950             this.el.on(this.autoHideHd);
35951         }
35952     },
35953
35954     clearAutoHide : function(){
35955         if(this.autoHide !== false){
35956             this.el.un("mouseout", this.autoHideHd.mouseout);
35957             this.el.un("mouseover", this.autoHideHd.mouseover);
35958         }
35959     },
35960
35961     clearMonitor : function(){
35962         Roo.get(document).un("click", this.slideInIf, this);
35963     },
35964
35965     // these names are backwards but not changed for compat
35966     slideOut : function(){
35967         if(this.isSlid || this.el.hasActiveFx()){
35968             return;
35969         }
35970         this.isSlid = true;
35971         if(this.collapseBtn){
35972             this.collapseBtn.hide();
35973         }
35974         this.closeBtnState = this.closeBtn.getStyle('display');
35975         this.closeBtn.hide();
35976         if(this.stickBtn){
35977             this.stickBtn.show();
35978         }
35979         this.el.show();
35980         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35981         this.beforeSlide();
35982         this.el.setStyle("z-index", 10001);
35983         this.el.slideIn(this.getSlideAnchor(), {
35984             callback: function(){
35985                 this.afterSlide();
35986                 this.initAutoHide();
35987                 Roo.get(document).on("click", this.slideInIf, this);
35988                 this.fireEvent("slideshow", this);
35989             },
35990             scope: this,
35991             block: true
35992         });
35993     },
35994
35995     afterSlideIn : function(){
35996         this.clearAutoHide();
35997         this.isSlid = false;
35998         this.clearMonitor();
35999         this.el.setStyle("z-index", "");
36000         if(this.collapseBtn){
36001             this.collapseBtn.show();
36002         }
36003         this.closeBtn.setStyle('display', this.closeBtnState);
36004         if(this.stickBtn){
36005             this.stickBtn.hide();
36006         }
36007         this.fireEvent("slidehide", this);
36008     },
36009
36010     slideIn : function(cb){
36011         if(!this.isSlid || this.el.hasActiveFx()){
36012             Roo.callback(cb);
36013             return;
36014         }
36015         this.isSlid = false;
36016         this.beforeSlide();
36017         this.el.slideOut(this.getSlideAnchor(), {
36018             callback: function(){
36019                 this.el.setLeftTop(-10000, -10000);
36020                 this.afterSlide();
36021                 this.afterSlideIn();
36022                 Roo.callback(cb);
36023             },
36024             scope: this,
36025             block: true
36026         });
36027     },
36028     
36029     slideInIf : function(e){
36030         if(!e.within(this.el)){
36031             this.slideIn();
36032         }
36033     },
36034
36035     animateCollapse : function(){
36036         this.beforeSlide();
36037         this.el.setStyle("z-index", 20000);
36038         var anchor = this.getSlideAnchor();
36039         this.el.slideOut(anchor, {
36040             callback : function(){
36041                 this.el.setStyle("z-index", "");
36042                 this.collapsedEl.slideIn(anchor, {duration:.3});
36043                 this.afterSlide();
36044                 this.el.setLocation(-10000,-10000);
36045                 this.el.hide();
36046                 this.fireEvent("collapsed", this);
36047             },
36048             scope: this,
36049             block: true
36050         });
36051     },
36052
36053     animateExpand : function(){
36054         this.beforeSlide();
36055         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36056         this.el.setStyle("z-index", 20000);
36057         this.collapsedEl.hide({
36058             duration:.1
36059         });
36060         this.el.slideIn(this.getSlideAnchor(), {
36061             callback : function(){
36062                 this.el.setStyle("z-index", "");
36063                 this.afterSlide();
36064                 if(this.split){
36065                     this.split.el.show();
36066                 }
36067                 this.fireEvent("invalidated", this);
36068                 this.fireEvent("expanded", this);
36069             },
36070             scope: this,
36071             block: true
36072         });
36073     },
36074
36075     anchors : {
36076         "west" : "left",
36077         "east" : "right",
36078         "north" : "top",
36079         "south" : "bottom"
36080     },
36081
36082     sanchors : {
36083         "west" : "l",
36084         "east" : "r",
36085         "north" : "t",
36086         "south" : "b"
36087     },
36088
36089     canchors : {
36090         "west" : "tl-tr",
36091         "east" : "tr-tl",
36092         "north" : "tl-bl",
36093         "south" : "bl-tl"
36094     },
36095
36096     getAnchor : function(){
36097         return this.anchors[this.position];
36098     },
36099
36100     getCollapseAnchor : function(){
36101         return this.canchors[this.position];
36102     },
36103
36104     getSlideAnchor : function(){
36105         return this.sanchors[this.position];
36106     },
36107
36108     getAlignAdj : function(){
36109         var cm = this.cmargins;
36110         switch(this.position){
36111             case "west":
36112                 return [0, 0];
36113             break;
36114             case "east":
36115                 return [0, 0];
36116             break;
36117             case "north":
36118                 return [0, 0];
36119             break;
36120             case "south":
36121                 return [0, 0];
36122             break;
36123         }
36124     },
36125
36126     getExpandAdj : function(){
36127         var c = this.collapsedEl, cm = this.cmargins;
36128         switch(this.position){
36129             case "west":
36130                 return [-(cm.right+c.getWidth()+cm.left), 0];
36131             break;
36132             case "east":
36133                 return [cm.right+c.getWidth()+cm.left, 0];
36134             break;
36135             case "north":
36136                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36137             break;
36138             case "south":
36139                 return [0, cm.top+cm.bottom+c.getHeight()];
36140             break;
36141         }
36142     }
36143 });/*
36144  * Based on:
36145  * Ext JS Library 1.1.1
36146  * Copyright(c) 2006-2007, Ext JS, LLC.
36147  *
36148  * Originally Released Under LGPL - original licence link has changed is not relivant.
36149  *
36150  * Fork - LGPL
36151  * <script type="text/javascript">
36152  */
36153 /*
36154  * These classes are private internal classes
36155  */
36156 Roo.bootstrap.layout.Center = function(config){
36157     config.region = "center";
36158     Roo.bootstrap.layout.Region.call(this, config);
36159     this.visible = true;
36160     this.minWidth = config.minWidth || 20;
36161     this.minHeight = config.minHeight || 20;
36162 };
36163
36164 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36165     hide : function(){
36166         // center panel can't be hidden
36167     },
36168     
36169     show : function(){
36170         // center panel can't be hidden
36171     },
36172     
36173     getMinWidth: function(){
36174         return this.minWidth;
36175     },
36176     
36177     getMinHeight: function(){
36178         return this.minHeight;
36179     }
36180 });
36181
36182
36183
36184
36185  
36186
36187
36188
36189
36190
36191 Roo.bootstrap.layout.North = function(config)
36192 {
36193     config.region = 'north';
36194     config.cursor = 'n-resize';
36195     
36196     Roo.bootstrap.layout.Split.call(this, config);
36197     
36198     
36199     if(this.split){
36200         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36201         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36202         this.split.el.addClass("roo-layout-split-v");
36203     }
36204     var size = config.initialSize || config.height;
36205     if(typeof size != "undefined"){
36206         this.el.setHeight(size);
36207     }
36208 };
36209 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36210 {
36211     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36212     
36213     
36214     
36215     getBox : function(){
36216         if(this.collapsed){
36217             return this.collapsedEl.getBox();
36218         }
36219         var box = this.el.getBox();
36220         if(this.split){
36221             box.height += this.split.el.getHeight();
36222         }
36223         return box;
36224     },
36225     
36226     updateBox : function(box){
36227         if(this.split && !this.collapsed){
36228             box.height -= this.split.el.getHeight();
36229             this.split.el.setLeft(box.x);
36230             this.split.el.setTop(box.y+box.height);
36231             this.split.el.setWidth(box.width);
36232         }
36233         if(this.collapsed){
36234             this.updateBody(box.width, null);
36235         }
36236         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36237     }
36238 });
36239
36240
36241
36242
36243
36244 Roo.bootstrap.layout.South = function(config){
36245     config.region = 'south';
36246     config.cursor = 's-resize';
36247     Roo.bootstrap.layout.Split.call(this, config);
36248     if(this.split){
36249         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36250         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36251         this.split.el.addClass("roo-layout-split-v");
36252     }
36253     var size = config.initialSize || config.height;
36254     if(typeof size != "undefined"){
36255         this.el.setHeight(size);
36256     }
36257 };
36258
36259 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36260     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36261     getBox : function(){
36262         if(this.collapsed){
36263             return this.collapsedEl.getBox();
36264         }
36265         var box = this.el.getBox();
36266         if(this.split){
36267             var sh = this.split.el.getHeight();
36268             box.height += sh;
36269             box.y -= sh;
36270         }
36271         return box;
36272     },
36273     
36274     updateBox : function(box){
36275         if(this.split && !this.collapsed){
36276             var sh = this.split.el.getHeight();
36277             box.height -= sh;
36278             box.y += sh;
36279             this.split.el.setLeft(box.x);
36280             this.split.el.setTop(box.y-sh);
36281             this.split.el.setWidth(box.width);
36282         }
36283         if(this.collapsed){
36284             this.updateBody(box.width, null);
36285         }
36286         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36287     }
36288 });
36289
36290 Roo.bootstrap.layout.East = function(config){
36291     config.region = "east";
36292     config.cursor = "e-resize";
36293     Roo.bootstrap.layout.Split.call(this, config);
36294     if(this.split){
36295         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36296         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36297         this.split.el.addClass("roo-layout-split-h");
36298     }
36299     var size = config.initialSize || config.width;
36300     if(typeof size != "undefined"){
36301         this.el.setWidth(size);
36302     }
36303 };
36304 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36305     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36306     getBox : function(){
36307         if(this.collapsed){
36308             return this.collapsedEl.getBox();
36309         }
36310         var box = this.el.getBox();
36311         if(this.split){
36312             var sw = this.split.el.getWidth();
36313             box.width += sw;
36314             box.x -= sw;
36315         }
36316         return box;
36317     },
36318
36319     updateBox : function(box){
36320         if(this.split && !this.collapsed){
36321             var sw = this.split.el.getWidth();
36322             box.width -= sw;
36323             this.split.el.setLeft(box.x);
36324             this.split.el.setTop(box.y);
36325             this.split.el.setHeight(box.height);
36326             box.x += sw;
36327         }
36328         if(this.collapsed){
36329             this.updateBody(null, box.height);
36330         }
36331         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36332     }
36333 });
36334
36335 Roo.bootstrap.layout.West = function(config){
36336     config.region = "west";
36337     config.cursor = "w-resize";
36338     
36339     Roo.bootstrap.layout.Split.call(this, config);
36340     if(this.split){
36341         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36342         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36343         this.split.el.addClass("roo-layout-split-h");
36344     }
36345     
36346 };
36347 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36348     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36349     
36350     onRender: function(ctr, pos)
36351     {
36352         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36353         var size = this.config.initialSize || this.config.width;
36354         if(typeof size != "undefined"){
36355             this.el.setWidth(size);
36356         }
36357     },
36358     
36359     getBox : function(){
36360         if(this.collapsed){
36361             return this.collapsedEl.getBox();
36362         }
36363         var box = this.el.getBox();
36364         if(this.split){
36365             box.width += this.split.el.getWidth();
36366         }
36367         return box;
36368     },
36369     
36370     updateBox : function(box){
36371         if(this.split && !this.collapsed){
36372             var sw = this.split.el.getWidth();
36373             box.width -= sw;
36374             this.split.el.setLeft(box.x+box.width);
36375             this.split.el.setTop(box.y);
36376             this.split.el.setHeight(box.height);
36377         }
36378         if(this.collapsed){
36379             this.updateBody(null, box.height);
36380         }
36381         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36382     }
36383 });
36384 Roo.namespace("Roo.bootstrap.panel");/*
36385  * Based on:
36386  * Ext JS Library 1.1.1
36387  * Copyright(c) 2006-2007, Ext JS, LLC.
36388  *
36389  * Originally Released Under LGPL - original licence link has changed is not relivant.
36390  *
36391  * Fork - LGPL
36392  * <script type="text/javascript">
36393  */
36394 /**
36395  * @class Roo.ContentPanel
36396  * @extends Roo.util.Observable
36397  * A basic ContentPanel element.
36398  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36399  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36400  * @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
36401  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36402  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36403  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36404  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36405  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36406  * @cfg {String} title          The title for this panel
36407  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36408  * @cfg {String} url            Calls {@link #setUrl} with this value
36409  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36410  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36411  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36412  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36413  * @cfg {Boolean} badges render the badges
36414
36415  * @constructor
36416  * Create a new ContentPanel.
36417  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36418  * @param {String/Object} config A string to set only the title or a config object
36419  * @param {String} content (optional) Set the HTML content for this panel
36420  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36421  */
36422 Roo.bootstrap.panel.Content = function( config){
36423     
36424     this.tpl = config.tpl || false;
36425     
36426     var el = config.el;
36427     var content = config.content;
36428
36429     if(config.autoCreate){ // xtype is available if this is called from factory
36430         el = Roo.id();
36431     }
36432     this.el = Roo.get(el);
36433     if(!this.el && config && config.autoCreate){
36434         if(typeof config.autoCreate == "object"){
36435             if(!config.autoCreate.id){
36436                 config.autoCreate.id = config.id||el;
36437             }
36438             this.el = Roo.DomHelper.append(document.body,
36439                         config.autoCreate, true);
36440         }else{
36441             var elcfg =  {   tag: "div",
36442                             cls: "roo-layout-inactive-content",
36443                             id: config.id||el
36444                             };
36445             if (config.html) {
36446                 elcfg.html = config.html;
36447                 
36448             }
36449                         
36450             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36451         }
36452     } 
36453     this.closable = false;
36454     this.loaded = false;
36455     this.active = false;
36456    
36457       
36458     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36459         
36460         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36461         
36462         this.wrapEl = this.el; //this.el.wrap();
36463         var ti = [];
36464         if (config.toolbar.items) {
36465             ti = config.toolbar.items ;
36466             delete config.toolbar.items ;
36467         }
36468         
36469         var nitems = [];
36470         this.toolbar.render(this.wrapEl, 'before');
36471         for(var i =0;i < ti.length;i++) {
36472           //  Roo.log(['add child', items[i]]);
36473             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36474         }
36475         this.toolbar.items = nitems;
36476         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36477         delete config.toolbar;
36478         
36479     }
36480     /*
36481     // xtype created footer. - not sure if will work as we normally have to render first..
36482     if (this.footer && !this.footer.el && this.footer.xtype) {
36483         if (!this.wrapEl) {
36484             this.wrapEl = this.el.wrap();
36485         }
36486     
36487         this.footer.container = this.wrapEl.createChild();
36488          
36489         this.footer = Roo.factory(this.footer, Roo);
36490         
36491     }
36492     */
36493     
36494      if(typeof config == "string"){
36495         this.title = config;
36496     }else{
36497         Roo.apply(this, config);
36498     }
36499     
36500     if(this.resizeEl){
36501         this.resizeEl = Roo.get(this.resizeEl, true);
36502     }else{
36503         this.resizeEl = this.el;
36504     }
36505     // handle view.xtype
36506     
36507  
36508     
36509     
36510     this.addEvents({
36511         /**
36512          * @event activate
36513          * Fires when this panel is activated. 
36514          * @param {Roo.ContentPanel} this
36515          */
36516         "activate" : true,
36517         /**
36518          * @event deactivate
36519          * Fires when this panel is activated. 
36520          * @param {Roo.ContentPanel} this
36521          */
36522         "deactivate" : true,
36523
36524         /**
36525          * @event resize
36526          * Fires when this panel is resized if fitToFrame is true.
36527          * @param {Roo.ContentPanel} this
36528          * @param {Number} width The width after any component adjustments
36529          * @param {Number} height The height after any component adjustments
36530          */
36531         "resize" : true,
36532         
36533          /**
36534          * @event render
36535          * Fires when this tab is created
36536          * @param {Roo.ContentPanel} this
36537          */
36538         "render" : true
36539         
36540         
36541         
36542     });
36543     
36544
36545     
36546     
36547     if(this.autoScroll){
36548         this.resizeEl.setStyle("overflow", "auto");
36549     } else {
36550         // fix randome scrolling
36551         //this.el.on('scroll', function() {
36552         //    Roo.log('fix random scolling');
36553         //    this.scrollTo('top',0); 
36554         //});
36555     }
36556     content = content || this.content;
36557     if(content){
36558         this.setContent(content);
36559     }
36560     if(config && config.url){
36561         this.setUrl(this.url, this.params, this.loadOnce);
36562     }
36563     
36564     
36565     
36566     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36567     
36568     if (this.view && typeof(this.view.xtype) != 'undefined') {
36569         this.view.el = this.el.appendChild(document.createElement("div"));
36570         this.view = Roo.factory(this.view); 
36571         this.view.render  &&  this.view.render(false, '');  
36572     }
36573     
36574     
36575     this.fireEvent('render', this);
36576 };
36577
36578 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36579     
36580     tabTip : '',
36581     
36582     setRegion : function(region){
36583         this.region = region;
36584         this.setActiveClass(region && !this.background);
36585     },
36586     
36587     
36588     setActiveClass: function(state)
36589     {
36590         if(state){
36591            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36592            this.el.setStyle('position','relative');
36593         }else{
36594            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36595            this.el.setStyle('position', 'absolute');
36596         } 
36597     },
36598     
36599     /**
36600      * Returns the toolbar for this Panel if one was configured. 
36601      * @return {Roo.Toolbar} 
36602      */
36603     getToolbar : function(){
36604         return this.toolbar;
36605     },
36606     
36607     setActiveState : function(active)
36608     {
36609         this.active = active;
36610         this.setActiveClass(active);
36611         if(!active){
36612             if(this.fireEvent("deactivate", this) === false){
36613                 return false;
36614             }
36615             return true;
36616         }
36617         this.fireEvent("activate", this);
36618         return true;
36619     },
36620     /**
36621      * Updates this panel's element
36622      * @param {String} content The new content
36623      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36624     */
36625     setContent : function(content, loadScripts){
36626         this.el.update(content, loadScripts);
36627     },
36628
36629     ignoreResize : function(w, h){
36630         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36631             return true;
36632         }else{
36633             this.lastSize = {width: w, height: h};
36634             return false;
36635         }
36636     },
36637     /**
36638      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36639      * @return {Roo.UpdateManager} The UpdateManager
36640      */
36641     getUpdateManager : function(){
36642         return this.el.getUpdateManager();
36643     },
36644      /**
36645      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36646      * @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:
36647 <pre><code>
36648 panel.load({
36649     url: "your-url.php",
36650     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36651     callback: yourFunction,
36652     scope: yourObject, //(optional scope)
36653     discardUrl: false,
36654     nocache: false,
36655     text: "Loading...",
36656     timeout: 30,
36657     scripts: false
36658 });
36659 </code></pre>
36660      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36661      * 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.
36662      * @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}
36663      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36664      * @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.
36665      * @return {Roo.ContentPanel} this
36666      */
36667     load : function(){
36668         var um = this.el.getUpdateManager();
36669         um.update.apply(um, arguments);
36670         return this;
36671     },
36672
36673
36674     /**
36675      * 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.
36676      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36677      * @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)
36678      * @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)
36679      * @return {Roo.UpdateManager} The UpdateManager
36680      */
36681     setUrl : function(url, params, loadOnce){
36682         if(this.refreshDelegate){
36683             this.removeListener("activate", this.refreshDelegate);
36684         }
36685         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36686         this.on("activate", this.refreshDelegate);
36687         return this.el.getUpdateManager();
36688     },
36689     
36690     _handleRefresh : function(url, params, loadOnce){
36691         if(!loadOnce || !this.loaded){
36692             var updater = this.el.getUpdateManager();
36693             updater.update(url, params, this._setLoaded.createDelegate(this));
36694         }
36695     },
36696     
36697     _setLoaded : function(){
36698         this.loaded = true;
36699     }, 
36700     
36701     /**
36702      * Returns this panel's id
36703      * @return {String} 
36704      */
36705     getId : function(){
36706         return this.el.id;
36707     },
36708     
36709     /** 
36710      * Returns this panel's element - used by regiosn to add.
36711      * @return {Roo.Element} 
36712      */
36713     getEl : function(){
36714         return this.wrapEl || this.el;
36715     },
36716     
36717    
36718     
36719     adjustForComponents : function(width, height)
36720     {
36721         //Roo.log('adjustForComponents ');
36722         if(this.resizeEl != this.el){
36723             width -= this.el.getFrameWidth('lr');
36724             height -= this.el.getFrameWidth('tb');
36725         }
36726         if(this.toolbar){
36727             var te = this.toolbar.getEl();
36728             te.setWidth(width);
36729             height -= te.getHeight();
36730         }
36731         if(this.footer){
36732             var te = this.footer.getEl();
36733             te.setWidth(width);
36734             height -= te.getHeight();
36735         }
36736         
36737         
36738         if(this.adjustments){
36739             width += this.adjustments[0];
36740             height += this.adjustments[1];
36741         }
36742         return {"width": width, "height": height};
36743     },
36744     
36745     setSize : function(width, height){
36746         if(this.fitToFrame && !this.ignoreResize(width, height)){
36747             if(this.fitContainer && this.resizeEl != this.el){
36748                 this.el.setSize(width, height);
36749             }
36750             var size = this.adjustForComponents(width, height);
36751             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36752             this.fireEvent('resize', this, size.width, size.height);
36753         }
36754     },
36755     
36756     /**
36757      * Returns this panel's title
36758      * @return {String} 
36759      */
36760     getTitle : function(){
36761         
36762         if (typeof(this.title) != 'object') {
36763             return this.title;
36764         }
36765         
36766         var t = '';
36767         for (var k in this.title) {
36768             if (!this.title.hasOwnProperty(k)) {
36769                 continue;
36770             }
36771             
36772             if (k.indexOf('-') >= 0) {
36773                 var s = k.split('-');
36774                 for (var i = 0; i<s.length; i++) {
36775                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36776                 }
36777             } else {
36778                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36779             }
36780         }
36781         return t;
36782     },
36783     
36784     /**
36785      * Set this panel's title
36786      * @param {String} title
36787      */
36788     setTitle : function(title){
36789         this.title = title;
36790         if(this.region){
36791             this.region.updatePanelTitle(this, title);
36792         }
36793     },
36794     
36795     /**
36796      * Returns true is this panel was configured to be closable
36797      * @return {Boolean} 
36798      */
36799     isClosable : function(){
36800         return this.closable;
36801     },
36802     
36803     beforeSlide : function(){
36804         this.el.clip();
36805         this.resizeEl.clip();
36806     },
36807     
36808     afterSlide : function(){
36809         this.el.unclip();
36810         this.resizeEl.unclip();
36811     },
36812     
36813     /**
36814      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36815      *   Will fail silently if the {@link #setUrl} method has not been called.
36816      *   This does not activate the panel, just updates its content.
36817      */
36818     refresh : function(){
36819         if(this.refreshDelegate){
36820            this.loaded = false;
36821            this.refreshDelegate();
36822         }
36823     },
36824     
36825     /**
36826      * Destroys this panel
36827      */
36828     destroy : function(){
36829         this.el.removeAllListeners();
36830         var tempEl = document.createElement("span");
36831         tempEl.appendChild(this.el.dom);
36832         tempEl.innerHTML = "";
36833         this.el.remove();
36834         this.el = null;
36835     },
36836     
36837     /**
36838      * form - if the content panel contains a form - this is a reference to it.
36839      * @type {Roo.form.Form}
36840      */
36841     form : false,
36842     /**
36843      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36844      *    This contains a reference to it.
36845      * @type {Roo.View}
36846      */
36847     view : false,
36848     
36849       /**
36850      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36851      * <pre><code>
36852
36853 layout.addxtype({
36854        xtype : 'Form',
36855        items: [ .... ]
36856    }
36857 );
36858
36859 </code></pre>
36860      * @param {Object} cfg Xtype definition of item to add.
36861      */
36862     
36863     
36864     getChildContainer: function () {
36865         return this.getEl();
36866     }
36867     
36868     
36869     /*
36870         var  ret = new Roo.factory(cfg);
36871         return ret;
36872         
36873         
36874         // add form..
36875         if (cfg.xtype.match(/^Form$/)) {
36876             
36877             var el;
36878             //if (this.footer) {
36879             //    el = this.footer.container.insertSibling(false, 'before');
36880             //} else {
36881                 el = this.el.createChild();
36882             //}
36883
36884             this.form = new  Roo.form.Form(cfg);
36885             
36886             
36887             if ( this.form.allItems.length) {
36888                 this.form.render(el.dom);
36889             }
36890             return this.form;
36891         }
36892         // should only have one of theses..
36893         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36894             // views.. should not be just added - used named prop 'view''
36895             
36896             cfg.el = this.el.appendChild(document.createElement("div"));
36897             // factory?
36898             
36899             var ret = new Roo.factory(cfg);
36900              
36901              ret.render && ret.render(false, ''); // render blank..
36902             this.view = ret;
36903             return ret;
36904         }
36905         return false;
36906     }
36907     \*/
36908 });
36909  
36910 /**
36911  * @class Roo.bootstrap.panel.Grid
36912  * @extends Roo.bootstrap.panel.Content
36913  * @constructor
36914  * Create a new GridPanel.
36915  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36916  * @param {Object} config A the config object
36917   
36918  */
36919
36920
36921
36922 Roo.bootstrap.panel.Grid = function(config)
36923 {
36924     
36925       
36926     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36927         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36928
36929     config.el = this.wrapper;
36930     //this.el = this.wrapper;
36931     
36932       if (config.container) {
36933         // ctor'ed from a Border/panel.grid
36934         
36935         
36936         this.wrapper.setStyle("overflow", "hidden");
36937         this.wrapper.addClass('roo-grid-container');
36938
36939     }
36940     
36941     
36942     if(config.toolbar){
36943         var tool_el = this.wrapper.createChild();    
36944         this.toolbar = Roo.factory(config.toolbar);
36945         var ti = [];
36946         if (config.toolbar.items) {
36947             ti = config.toolbar.items ;
36948             delete config.toolbar.items ;
36949         }
36950         
36951         var nitems = [];
36952         this.toolbar.render(tool_el);
36953         for(var i =0;i < ti.length;i++) {
36954           //  Roo.log(['add child', items[i]]);
36955             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36956         }
36957         this.toolbar.items = nitems;
36958         
36959         delete config.toolbar;
36960     }
36961     
36962     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36963     config.grid.scrollBody = true;;
36964     config.grid.monitorWindowResize = false; // turn off autosizing
36965     config.grid.autoHeight = false;
36966     config.grid.autoWidth = false;
36967     
36968     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36969     
36970     if (config.background) {
36971         // render grid on panel activation (if panel background)
36972         this.on('activate', function(gp) {
36973             if (!gp.grid.rendered) {
36974                 gp.grid.render(this.wrapper);
36975                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36976             }
36977         });
36978             
36979     } else {
36980         this.grid.render(this.wrapper);
36981         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36982
36983     }
36984     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36985     // ??? needed ??? config.el = this.wrapper;
36986     
36987     
36988     
36989   
36990     // xtype created footer. - not sure if will work as we normally have to render first..
36991     if (this.footer && !this.footer.el && this.footer.xtype) {
36992         
36993         var ctr = this.grid.getView().getFooterPanel(true);
36994         this.footer.dataSource = this.grid.dataSource;
36995         this.footer = Roo.factory(this.footer, Roo);
36996         this.footer.render(ctr);
36997         
36998     }
36999     
37000     
37001     
37002     
37003      
37004 };
37005
37006 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37007     getId : function(){
37008         return this.grid.id;
37009     },
37010     
37011     /**
37012      * Returns the grid for this panel
37013      * @return {Roo.bootstrap.Table} 
37014      */
37015     getGrid : function(){
37016         return this.grid;    
37017     },
37018     
37019     setSize : function(width, height){
37020         if(!this.ignoreResize(width, height)){
37021             var grid = this.grid;
37022             var size = this.adjustForComponents(width, height);
37023             var gridel = grid.getGridEl();
37024             gridel.setSize(size.width, size.height);
37025             /*
37026             var thd = grid.getGridEl().select('thead',true).first();
37027             var tbd = grid.getGridEl().select('tbody', true).first();
37028             if (tbd) {
37029                 tbd.setSize(width, height - thd.getHeight());
37030             }
37031             */
37032             grid.autoSize();
37033         }
37034     },
37035      
37036     
37037     
37038     beforeSlide : function(){
37039         this.grid.getView().scroller.clip();
37040     },
37041     
37042     afterSlide : function(){
37043         this.grid.getView().scroller.unclip();
37044     },
37045     
37046     destroy : function(){
37047         this.grid.destroy();
37048         delete this.grid;
37049         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37050     }
37051 });
37052
37053 /**
37054  * @class Roo.bootstrap.panel.Nest
37055  * @extends Roo.bootstrap.panel.Content
37056  * @constructor
37057  * Create a new Panel, that can contain a layout.Border.
37058  * 
37059  * 
37060  * @param {Roo.BorderLayout} layout The layout for this panel
37061  * @param {String/Object} config A string to set only the title or a config object
37062  */
37063 Roo.bootstrap.panel.Nest = function(config)
37064 {
37065     // construct with only one argument..
37066     /* FIXME - implement nicer consturctors
37067     if (layout.layout) {
37068         config = layout;
37069         layout = config.layout;
37070         delete config.layout;
37071     }
37072     if (layout.xtype && !layout.getEl) {
37073         // then layout needs constructing..
37074         layout = Roo.factory(layout, Roo);
37075     }
37076     */
37077     
37078     config.el =  config.layout.getEl();
37079     
37080     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37081     
37082     config.layout.monitorWindowResize = false; // turn off autosizing
37083     this.layout = config.layout;
37084     this.layout.getEl().addClass("roo-layout-nested-layout");
37085     
37086     
37087     
37088     
37089 };
37090
37091 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37092
37093     setSize : function(width, height){
37094         if(!this.ignoreResize(width, height)){
37095             var size = this.adjustForComponents(width, height);
37096             var el = this.layout.getEl();
37097             if (size.height < 1) {
37098                 el.setWidth(size.width);   
37099             } else {
37100                 el.setSize(size.width, size.height);
37101             }
37102             var touch = el.dom.offsetWidth;
37103             this.layout.layout();
37104             // ie requires a double layout on the first pass
37105             if(Roo.isIE && !this.initialized){
37106                 this.initialized = true;
37107                 this.layout.layout();
37108             }
37109         }
37110     },
37111     
37112     // activate all subpanels if not currently active..
37113     
37114     setActiveState : function(active){
37115         this.active = active;
37116         this.setActiveClass(active);
37117         
37118         if(!active){
37119             this.fireEvent("deactivate", this);
37120             return;
37121         }
37122         
37123         this.fireEvent("activate", this);
37124         // not sure if this should happen before or after..
37125         if (!this.layout) {
37126             return; // should not happen..
37127         }
37128         var reg = false;
37129         for (var r in this.layout.regions) {
37130             reg = this.layout.getRegion(r);
37131             if (reg.getActivePanel()) {
37132                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37133                 reg.setActivePanel(reg.getActivePanel());
37134                 continue;
37135             }
37136             if (!reg.panels.length) {
37137                 continue;
37138             }
37139             reg.showPanel(reg.getPanel(0));
37140         }
37141         
37142         
37143         
37144         
37145     },
37146     
37147     /**
37148      * Returns the nested BorderLayout for this panel
37149      * @return {Roo.BorderLayout} 
37150      */
37151     getLayout : function(){
37152         return this.layout;
37153     },
37154     
37155      /**
37156      * Adds a xtype elements to the layout of the nested panel
37157      * <pre><code>
37158
37159 panel.addxtype({
37160        xtype : 'ContentPanel',
37161        region: 'west',
37162        items: [ .... ]
37163    }
37164 );
37165
37166 panel.addxtype({
37167         xtype : 'NestedLayoutPanel',
37168         region: 'west',
37169         layout: {
37170            center: { },
37171            west: { }   
37172         },
37173         items : [ ... list of content panels or nested layout panels.. ]
37174    }
37175 );
37176 </code></pre>
37177      * @param {Object} cfg Xtype definition of item to add.
37178      */
37179     addxtype : function(cfg) {
37180         return this.layout.addxtype(cfg);
37181     
37182     }
37183 });        /*
37184  * Based on:
37185  * Ext JS Library 1.1.1
37186  * Copyright(c) 2006-2007, Ext JS, LLC.
37187  *
37188  * Originally Released Under LGPL - original licence link has changed is not relivant.
37189  *
37190  * Fork - LGPL
37191  * <script type="text/javascript">
37192  */
37193 /**
37194  * @class Roo.TabPanel
37195  * @extends Roo.util.Observable
37196  * A lightweight tab container.
37197  * <br><br>
37198  * Usage:
37199  * <pre><code>
37200 // basic tabs 1, built from existing content
37201 var tabs = new Roo.TabPanel("tabs1");
37202 tabs.addTab("script", "View Script");
37203 tabs.addTab("markup", "View Markup");
37204 tabs.activate("script");
37205
37206 // more advanced tabs, built from javascript
37207 var jtabs = new Roo.TabPanel("jtabs");
37208 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37209
37210 // set up the UpdateManager
37211 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37212 var updater = tab2.getUpdateManager();
37213 updater.setDefaultUrl("ajax1.htm");
37214 tab2.on('activate', updater.refresh, updater, true);
37215
37216 // Use setUrl for Ajax loading
37217 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37218 tab3.setUrl("ajax2.htm", null, true);
37219
37220 // Disabled tab
37221 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37222 tab4.disable();
37223
37224 jtabs.activate("jtabs-1");
37225  * </code></pre>
37226  * @constructor
37227  * Create a new TabPanel.
37228  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37229  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37230  */
37231 Roo.bootstrap.panel.Tabs = function(config){
37232     /**
37233     * The container element for this TabPanel.
37234     * @type Roo.Element
37235     */
37236     this.el = Roo.get(config.el);
37237     delete config.el;
37238     if(config){
37239         if(typeof config == "boolean"){
37240             this.tabPosition = config ? "bottom" : "top";
37241         }else{
37242             Roo.apply(this, config);
37243         }
37244     }
37245     
37246     if(this.tabPosition == "bottom"){
37247         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37248         this.el.addClass("roo-tabs-bottom");
37249     }
37250     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37251     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37252     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37253     if(Roo.isIE){
37254         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37255     }
37256     if(this.tabPosition != "bottom"){
37257         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37258          * @type Roo.Element
37259          */
37260         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37261         this.el.addClass("roo-tabs-top");
37262     }
37263     this.items = [];
37264
37265     this.bodyEl.setStyle("position", "relative");
37266
37267     this.active = null;
37268     this.activateDelegate = this.activate.createDelegate(this);
37269
37270     this.addEvents({
37271         /**
37272          * @event tabchange
37273          * Fires when the active tab changes
37274          * @param {Roo.TabPanel} this
37275          * @param {Roo.TabPanelItem} activePanel The new active tab
37276          */
37277         "tabchange": true,
37278         /**
37279          * @event beforetabchange
37280          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37281          * @param {Roo.TabPanel} this
37282          * @param {Object} e Set cancel to true on this object to cancel the tab change
37283          * @param {Roo.TabPanelItem} tab The tab being changed to
37284          */
37285         "beforetabchange" : true
37286     });
37287
37288     Roo.EventManager.onWindowResize(this.onResize, this);
37289     this.cpad = this.el.getPadding("lr");
37290     this.hiddenCount = 0;
37291
37292
37293     // toolbar on the tabbar support...
37294     if (this.toolbar) {
37295         alert("no toolbar support yet");
37296         this.toolbar  = false;
37297         /*
37298         var tcfg = this.toolbar;
37299         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37300         this.toolbar = new Roo.Toolbar(tcfg);
37301         if (Roo.isSafari) {
37302             var tbl = tcfg.container.child('table', true);
37303             tbl.setAttribute('width', '100%');
37304         }
37305         */
37306         
37307     }
37308    
37309
37310
37311     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37312 };
37313
37314 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37315     /*
37316      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37317      */
37318     tabPosition : "top",
37319     /*
37320      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37321      */
37322     currentTabWidth : 0,
37323     /*
37324      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37325      */
37326     minTabWidth : 40,
37327     /*
37328      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37329      */
37330     maxTabWidth : 250,
37331     /*
37332      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37333      */
37334     preferredTabWidth : 175,
37335     /*
37336      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37337      */
37338     resizeTabs : false,
37339     /*
37340      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37341      */
37342     monitorResize : true,
37343     /*
37344      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37345      */
37346     toolbar : false,
37347
37348     /**
37349      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37350      * @param {String} id The id of the div to use <b>or create</b>
37351      * @param {String} text The text for the tab
37352      * @param {String} content (optional) Content to put in the TabPanelItem body
37353      * @param {Boolean} closable (optional) True to create a close icon on the tab
37354      * @return {Roo.TabPanelItem} The created TabPanelItem
37355      */
37356     addTab : function(id, text, content, closable, tpl)
37357     {
37358         var item = new Roo.bootstrap.panel.TabItem({
37359             panel: this,
37360             id : id,
37361             text : text,
37362             closable : closable,
37363             tpl : tpl
37364         });
37365         this.addTabItem(item);
37366         if(content){
37367             item.setContent(content);
37368         }
37369         return item;
37370     },
37371
37372     /**
37373      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37374      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37375      * @return {Roo.TabPanelItem}
37376      */
37377     getTab : function(id){
37378         return this.items[id];
37379     },
37380
37381     /**
37382      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37383      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37384      */
37385     hideTab : function(id){
37386         var t = this.items[id];
37387         if(!t.isHidden()){
37388            t.setHidden(true);
37389            this.hiddenCount++;
37390            this.autoSizeTabs();
37391         }
37392     },
37393
37394     /**
37395      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37396      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37397      */
37398     unhideTab : function(id){
37399         var t = this.items[id];
37400         if(t.isHidden()){
37401            t.setHidden(false);
37402            this.hiddenCount--;
37403            this.autoSizeTabs();
37404         }
37405     },
37406
37407     /**
37408      * Adds an existing {@link Roo.TabPanelItem}.
37409      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37410      */
37411     addTabItem : function(item){
37412         this.items[item.id] = item;
37413         this.items.push(item);
37414       //  if(this.resizeTabs){
37415     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37416   //         this.autoSizeTabs();
37417 //        }else{
37418 //            item.autoSize();
37419        // }
37420     },
37421
37422     /**
37423      * Removes a {@link Roo.TabPanelItem}.
37424      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37425      */
37426     removeTab : function(id){
37427         var items = this.items;
37428         var tab = items[id];
37429         if(!tab) { return; }
37430         var index = items.indexOf(tab);
37431         if(this.active == tab && items.length > 1){
37432             var newTab = this.getNextAvailable(index);
37433             if(newTab) {
37434                 newTab.activate();
37435             }
37436         }
37437         this.stripEl.dom.removeChild(tab.pnode.dom);
37438         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37439             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37440         }
37441         items.splice(index, 1);
37442         delete this.items[tab.id];
37443         tab.fireEvent("close", tab);
37444         tab.purgeListeners();
37445         this.autoSizeTabs();
37446     },
37447
37448     getNextAvailable : function(start){
37449         var items = this.items;
37450         var index = start;
37451         // look for a next tab that will slide over to
37452         // replace the one being removed
37453         while(index < items.length){
37454             var item = items[++index];
37455             if(item && !item.isHidden()){
37456                 return item;
37457             }
37458         }
37459         // if one isn't found select the previous tab (on the left)
37460         index = start;
37461         while(index >= 0){
37462             var item = items[--index];
37463             if(item && !item.isHidden()){
37464                 return item;
37465             }
37466         }
37467         return null;
37468     },
37469
37470     /**
37471      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37472      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37473      */
37474     disableTab : function(id){
37475         var tab = this.items[id];
37476         if(tab && this.active != tab){
37477             tab.disable();
37478         }
37479     },
37480
37481     /**
37482      * Enables a {@link Roo.TabPanelItem} that is disabled.
37483      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37484      */
37485     enableTab : function(id){
37486         var tab = this.items[id];
37487         tab.enable();
37488     },
37489
37490     /**
37491      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37492      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37493      * @return {Roo.TabPanelItem} The TabPanelItem.
37494      */
37495     activate : function(id){
37496         var tab = this.items[id];
37497         if(!tab){
37498             return null;
37499         }
37500         if(tab == this.active || tab.disabled){
37501             return tab;
37502         }
37503         var e = {};
37504         this.fireEvent("beforetabchange", this, e, tab);
37505         if(e.cancel !== true && !tab.disabled){
37506             if(this.active){
37507                 this.active.hide();
37508             }
37509             this.active = this.items[id];
37510             this.active.show();
37511             this.fireEvent("tabchange", this, this.active);
37512         }
37513         return tab;
37514     },
37515
37516     /**
37517      * Gets the active {@link Roo.TabPanelItem}.
37518      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37519      */
37520     getActiveTab : function(){
37521         return this.active;
37522     },
37523
37524     /**
37525      * Updates the tab body element to fit the height of the container element
37526      * for overflow scrolling
37527      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37528      */
37529     syncHeight : function(targetHeight){
37530         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37531         var bm = this.bodyEl.getMargins();
37532         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37533         this.bodyEl.setHeight(newHeight);
37534         return newHeight;
37535     },
37536
37537     onResize : function(){
37538         if(this.monitorResize){
37539             this.autoSizeTabs();
37540         }
37541     },
37542
37543     /**
37544      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37545      */
37546     beginUpdate : function(){
37547         this.updating = true;
37548     },
37549
37550     /**
37551      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37552      */
37553     endUpdate : function(){
37554         this.updating = false;
37555         this.autoSizeTabs();
37556     },
37557
37558     /**
37559      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37560      */
37561     autoSizeTabs : function(){
37562         var count = this.items.length;
37563         var vcount = count - this.hiddenCount;
37564         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37565             return;
37566         }
37567         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37568         var availWidth = Math.floor(w / vcount);
37569         var b = this.stripBody;
37570         if(b.getWidth() > w){
37571             var tabs = this.items;
37572             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37573             if(availWidth < this.minTabWidth){
37574                 /*if(!this.sleft){    // incomplete scrolling code
37575                     this.createScrollButtons();
37576                 }
37577                 this.showScroll();
37578                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37579             }
37580         }else{
37581             if(this.currentTabWidth < this.preferredTabWidth){
37582                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37583             }
37584         }
37585     },
37586
37587     /**
37588      * Returns the number of tabs in this TabPanel.
37589      * @return {Number}
37590      */
37591      getCount : function(){
37592          return this.items.length;
37593      },
37594
37595     /**
37596      * Resizes all the tabs to the passed width
37597      * @param {Number} The new width
37598      */
37599     setTabWidth : function(width){
37600         this.currentTabWidth = width;
37601         for(var i = 0, len = this.items.length; i < len; i++) {
37602                 if(!this.items[i].isHidden()) {
37603                 this.items[i].setWidth(width);
37604             }
37605         }
37606     },
37607
37608     /**
37609      * Destroys this TabPanel
37610      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37611      */
37612     destroy : function(removeEl){
37613         Roo.EventManager.removeResizeListener(this.onResize, this);
37614         for(var i = 0, len = this.items.length; i < len; i++){
37615             this.items[i].purgeListeners();
37616         }
37617         if(removeEl === true){
37618             this.el.update("");
37619             this.el.remove();
37620         }
37621     },
37622     
37623     createStrip : function(container)
37624     {
37625         var strip = document.createElement("nav");
37626         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37627         container.appendChild(strip);
37628         return strip;
37629     },
37630     
37631     createStripList : function(strip)
37632     {
37633         // div wrapper for retard IE
37634         // returns the "tr" element.
37635         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37636         //'<div class="x-tabs-strip-wrap">'+
37637           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37638           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37639         return strip.firstChild; //.firstChild.firstChild.firstChild;
37640     },
37641     createBody : function(container)
37642     {
37643         var body = document.createElement("div");
37644         Roo.id(body, "tab-body");
37645         //Roo.fly(body).addClass("x-tabs-body");
37646         Roo.fly(body).addClass("tab-content");
37647         container.appendChild(body);
37648         return body;
37649     },
37650     createItemBody :function(bodyEl, id){
37651         var body = Roo.getDom(id);
37652         if(!body){
37653             body = document.createElement("div");
37654             body.id = id;
37655         }
37656         //Roo.fly(body).addClass("x-tabs-item-body");
37657         Roo.fly(body).addClass("tab-pane");
37658          bodyEl.insertBefore(body, bodyEl.firstChild);
37659         return body;
37660     },
37661     /** @private */
37662     createStripElements :  function(stripEl, text, closable, tpl)
37663     {
37664         var td = document.createElement("li"); // was td..
37665         
37666         
37667         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37668         
37669         
37670         stripEl.appendChild(td);
37671         /*if(closable){
37672             td.className = "x-tabs-closable";
37673             if(!this.closeTpl){
37674                 this.closeTpl = new Roo.Template(
37675                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37676                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37677                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37678                 );
37679             }
37680             var el = this.closeTpl.overwrite(td, {"text": text});
37681             var close = el.getElementsByTagName("div")[0];
37682             var inner = el.getElementsByTagName("em")[0];
37683             return {"el": el, "close": close, "inner": inner};
37684         } else {
37685         */
37686         // not sure what this is..
37687 //            if(!this.tabTpl){
37688                 //this.tabTpl = new Roo.Template(
37689                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37690                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37691                 //);
37692 //                this.tabTpl = new Roo.Template(
37693 //                   '<a href="#">' +
37694 //                   '<span unselectable="on"' +
37695 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37696 //                            ' >{text}</span></a>'
37697 //                );
37698 //                
37699 //            }
37700
37701
37702             var template = tpl || this.tabTpl || false;
37703             
37704             if(!template){
37705                 
37706                 template = new Roo.Template(
37707                    '<a href="#">' +
37708                    '<span unselectable="on"' +
37709                             (this.disableTooltips ? '' : ' title="{text}"') +
37710                             ' >{text}</span></a>'
37711                 );
37712             }
37713             
37714             switch (typeof(template)) {
37715                 case 'object' :
37716                     break;
37717                 case 'string' :
37718                     template = new Roo.Template(template);
37719                     break;
37720                 default :
37721                     break;
37722             }
37723             
37724             var el = template.overwrite(td, {"text": text});
37725             
37726             var inner = el.getElementsByTagName("span")[0];
37727             
37728             return {"el": el, "inner": inner};
37729             
37730     }
37731         
37732     
37733 });
37734
37735 /**
37736  * @class Roo.TabPanelItem
37737  * @extends Roo.util.Observable
37738  * Represents an individual item (tab plus body) in a TabPanel.
37739  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37740  * @param {String} id The id of this TabPanelItem
37741  * @param {String} text The text for the tab of this TabPanelItem
37742  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37743  */
37744 Roo.bootstrap.panel.TabItem = function(config){
37745     /**
37746      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37747      * @type Roo.TabPanel
37748      */
37749     this.tabPanel = config.panel;
37750     /**
37751      * The id for this TabPanelItem
37752      * @type String
37753      */
37754     this.id = config.id;
37755     /** @private */
37756     this.disabled = false;
37757     /** @private */
37758     this.text = config.text;
37759     /** @private */
37760     this.loaded = false;
37761     this.closable = config.closable;
37762
37763     /**
37764      * The body element for this TabPanelItem.
37765      * @type Roo.Element
37766      */
37767     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37768     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37769     this.bodyEl.setStyle("display", "block");
37770     this.bodyEl.setStyle("zoom", "1");
37771     //this.hideAction();
37772
37773     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37774     /** @private */
37775     this.el = Roo.get(els.el);
37776     this.inner = Roo.get(els.inner, true);
37777     this.textEl = Roo.get(this.el.dom.firstChild, true);
37778     this.pnode = Roo.get(els.el.parentNode, true);
37779 //    this.el.on("mousedown", this.onTabMouseDown, this);
37780     this.el.on("click", this.onTabClick, this);
37781     /** @private */
37782     if(config.closable){
37783         var c = Roo.get(els.close, true);
37784         c.dom.title = this.closeText;
37785         c.addClassOnOver("close-over");
37786         c.on("click", this.closeClick, this);
37787      }
37788
37789     this.addEvents({
37790          /**
37791          * @event activate
37792          * Fires when this tab becomes the active tab.
37793          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37794          * @param {Roo.TabPanelItem} this
37795          */
37796         "activate": true,
37797         /**
37798          * @event beforeclose
37799          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37800          * @param {Roo.TabPanelItem} this
37801          * @param {Object} e Set cancel to true on this object to cancel the close.
37802          */
37803         "beforeclose": true,
37804         /**
37805          * @event close
37806          * Fires when this tab is closed.
37807          * @param {Roo.TabPanelItem} this
37808          */
37809          "close": true,
37810         /**
37811          * @event deactivate
37812          * Fires when this tab is no longer the active tab.
37813          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37814          * @param {Roo.TabPanelItem} this
37815          */
37816          "deactivate" : true
37817     });
37818     this.hidden = false;
37819
37820     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37821 };
37822
37823 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37824            {
37825     purgeListeners : function(){
37826        Roo.util.Observable.prototype.purgeListeners.call(this);
37827        this.el.removeAllListeners();
37828     },
37829     /**
37830      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37831      */
37832     show : function(){
37833         this.pnode.addClass("active");
37834         this.showAction();
37835         if(Roo.isOpera){
37836             this.tabPanel.stripWrap.repaint();
37837         }
37838         this.fireEvent("activate", this.tabPanel, this);
37839     },
37840
37841     /**
37842      * Returns true if this tab is the active tab.
37843      * @return {Boolean}
37844      */
37845     isActive : function(){
37846         return this.tabPanel.getActiveTab() == this;
37847     },
37848
37849     /**
37850      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37851      */
37852     hide : function(){
37853         this.pnode.removeClass("active");
37854         this.hideAction();
37855         this.fireEvent("deactivate", this.tabPanel, this);
37856     },
37857
37858     hideAction : function(){
37859         this.bodyEl.hide();
37860         this.bodyEl.setStyle("position", "absolute");
37861         this.bodyEl.setLeft("-20000px");
37862         this.bodyEl.setTop("-20000px");
37863     },
37864
37865     showAction : function(){
37866         this.bodyEl.setStyle("position", "relative");
37867         this.bodyEl.setTop("");
37868         this.bodyEl.setLeft("");
37869         this.bodyEl.show();
37870     },
37871
37872     /**
37873      * Set the tooltip for the tab.
37874      * @param {String} tooltip The tab's tooltip
37875      */
37876     setTooltip : function(text){
37877         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37878             this.textEl.dom.qtip = text;
37879             this.textEl.dom.removeAttribute('title');
37880         }else{
37881             this.textEl.dom.title = text;
37882         }
37883     },
37884
37885     onTabClick : function(e){
37886         e.preventDefault();
37887         this.tabPanel.activate(this.id);
37888     },
37889
37890     onTabMouseDown : function(e){
37891         e.preventDefault();
37892         this.tabPanel.activate(this.id);
37893     },
37894 /*
37895     getWidth : function(){
37896         return this.inner.getWidth();
37897     },
37898
37899     setWidth : function(width){
37900         var iwidth = width - this.pnode.getPadding("lr");
37901         this.inner.setWidth(iwidth);
37902         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37903         this.pnode.setWidth(width);
37904     },
37905 */
37906     /**
37907      * Show or hide the tab
37908      * @param {Boolean} hidden True to hide or false to show.
37909      */
37910     setHidden : function(hidden){
37911         this.hidden = hidden;
37912         this.pnode.setStyle("display", hidden ? "none" : "");
37913     },
37914
37915     /**
37916      * Returns true if this tab is "hidden"
37917      * @return {Boolean}
37918      */
37919     isHidden : function(){
37920         return this.hidden;
37921     },
37922
37923     /**
37924      * Returns the text for this tab
37925      * @return {String}
37926      */
37927     getText : function(){
37928         return this.text;
37929     },
37930     /*
37931     autoSize : function(){
37932         //this.el.beginMeasure();
37933         this.textEl.setWidth(1);
37934         /*
37935          *  #2804 [new] Tabs in Roojs
37936          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37937          */
37938         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37939         //this.el.endMeasure();
37940     //},
37941
37942     /**
37943      * Sets the text for the tab (Note: this also sets the tooltip text)
37944      * @param {String} text The tab's text and tooltip
37945      */
37946     setText : function(text){
37947         this.text = text;
37948         this.textEl.update(text);
37949         this.setTooltip(text);
37950         //if(!this.tabPanel.resizeTabs){
37951         //    this.autoSize();
37952         //}
37953     },
37954     /**
37955      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37956      */
37957     activate : function(){
37958         this.tabPanel.activate(this.id);
37959     },
37960
37961     /**
37962      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37963      */
37964     disable : function(){
37965         if(this.tabPanel.active != this){
37966             this.disabled = true;
37967             this.pnode.addClass("disabled");
37968         }
37969     },
37970
37971     /**
37972      * Enables this TabPanelItem if it was previously disabled.
37973      */
37974     enable : function(){
37975         this.disabled = false;
37976         this.pnode.removeClass("disabled");
37977     },
37978
37979     /**
37980      * Sets the content for this TabPanelItem.
37981      * @param {String} content The content
37982      * @param {Boolean} loadScripts true to look for and load scripts
37983      */
37984     setContent : function(content, loadScripts){
37985         this.bodyEl.update(content, loadScripts);
37986     },
37987
37988     /**
37989      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37990      * @return {Roo.UpdateManager} The UpdateManager
37991      */
37992     getUpdateManager : function(){
37993         return this.bodyEl.getUpdateManager();
37994     },
37995
37996     /**
37997      * Set a URL to be used to load the content for this TabPanelItem.
37998      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37999      * @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)
38000      * @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)
38001      * @return {Roo.UpdateManager} The UpdateManager
38002      */
38003     setUrl : function(url, params, loadOnce){
38004         if(this.refreshDelegate){
38005             this.un('activate', this.refreshDelegate);
38006         }
38007         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38008         this.on("activate", this.refreshDelegate);
38009         return this.bodyEl.getUpdateManager();
38010     },
38011
38012     /** @private */
38013     _handleRefresh : function(url, params, loadOnce){
38014         if(!loadOnce || !this.loaded){
38015             var updater = this.bodyEl.getUpdateManager();
38016             updater.update(url, params, this._setLoaded.createDelegate(this));
38017         }
38018     },
38019
38020     /**
38021      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38022      *   Will fail silently if the setUrl method has not been called.
38023      *   This does not activate the panel, just updates its content.
38024      */
38025     refresh : function(){
38026         if(this.refreshDelegate){
38027            this.loaded = false;
38028            this.refreshDelegate();
38029         }
38030     },
38031
38032     /** @private */
38033     _setLoaded : function(){
38034         this.loaded = true;
38035     },
38036
38037     /** @private */
38038     closeClick : function(e){
38039         var o = {};
38040         e.stopEvent();
38041         this.fireEvent("beforeclose", this, o);
38042         if(o.cancel !== true){
38043             this.tabPanel.removeTab(this.id);
38044         }
38045     },
38046     /**
38047      * The text displayed in the tooltip for the close icon.
38048      * @type String
38049      */
38050     closeText : "Close this tab"
38051 });
38052 /**
38053 *    This script refer to:
38054 *    Title: International Telephone Input
38055 *    Author: Jack O'Connor
38056 *    Code version:  v12.1.12
38057 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38058 **/
38059
38060 Roo.bootstrap.PhoneInputData = function() {
38061     var d = [
38062       [
38063         "Afghanistan (‫افغانستان‬‎)",
38064         "af",
38065         "93"
38066       ],
38067       [
38068         "Albania (Shqipëri)",
38069         "al",
38070         "355"
38071       ],
38072       [
38073         "Algeria (‫الجزائر‬‎)",
38074         "dz",
38075         "213"
38076       ],
38077       [
38078         "American Samoa",
38079         "as",
38080         "1684"
38081       ],
38082       [
38083         "Andorra",
38084         "ad",
38085         "376"
38086       ],
38087       [
38088         "Angola",
38089         "ao",
38090         "244"
38091       ],
38092       [
38093         "Anguilla",
38094         "ai",
38095         "1264"
38096       ],
38097       [
38098         "Antigua and Barbuda",
38099         "ag",
38100         "1268"
38101       ],
38102       [
38103         "Argentina",
38104         "ar",
38105         "54"
38106       ],
38107       [
38108         "Armenia (Հայաստան)",
38109         "am",
38110         "374"
38111       ],
38112       [
38113         "Aruba",
38114         "aw",
38115         "297"
38116       ],
38117       [
38118         "Australia",
38119         "au",
38120         "61",
38121         0
38122       ],
38123       [
38124         "Austria (Österreich)",
38125         "at",
38126         "43"
38127       ],
38128       [
38129         "Azerbaijan (Azərbaycan)",
38130         "az",
38131         "994"
38132       ],
38133       [
38134         "Bahamas",
38135         "bs",
38136         "1242"
38137       ],
38138       [
38139         "Bahrain (‫البحرين‬‎)",
38140         "bh",
38141         "973"
38142       ],
38143       [
38144         "Bangladesh (বাংলাদেশ)",
38145         "bd",
38146         "880"
38147       ],
38148       [
38149         "Barbados",
38150         "bb",
38151         "1246"
38152       ],
38153       [
38154         "Belarus (Беларусь)",
38155         "by",
38156         "375"
38157       ],
38158       [
38159         "Belgium (België)",
38160         "be",
38161         "32"
38162       ],
38163       [
38164         "Belize",
38165         "bz",
38166         "501"
38167       ],
38168       [
38169         "Benin (Bénin)",
38170         "bj",
38171         "229"
38172       ],
38173       [
38174         "Bermuda",
38175         "bm",
38176         "1441"
38177       ],
38178       [
38179         "Bhutan (འབྲུག)",
38180         "bt",
38181         "975"
38182       ],
38183       [
38184         "Bolivia",
38185         "bo",
38186         "591"
38187       ],
38188       [
38189         "Bosnia and Herzegovina (Босна и Херцеговина)",
38190         "ba",
38191         "387"
38192       ],
38193       [
38194         "Botswana",
38195         "bw",
38196         "267"
38197       ],
38198       [
38199         "Brazil (Brasil)",
38200         "br",
38201         "55"
38202       ],
38203       [
38204         "British Indian Ocean Territory",
38205         "io",
38206         "246"
38207       ],
38208       [
38209         "British Virgin Islands",
38210         "vg",
38211         "1284"
38212       ],
38213       [
38214         "Brunei",
38215         "bn",
38216         "673"
38217       ],
38218       [
38219         "Bulgaria (България)",
38220         "bg",
38221         "359"
38222       ],
38223       [
38224         "Burkina Faso",
38225         "bf",
38226         "226"
38227       ],
38228       [
38229         "Burundi (Uburundi)",
38230         "bi",
38231         "257"
38232       ],
38233       [
38234         "Cambodia (កម្ពុជា)",
38235         "kh",
38236         "855"
38237       ],
38238       [
38239         "Cameroon (Cameroun)",
38240         "cm",
38241         "237"
38242       ],
38243       [
38244         "Canada",
38245         "ca",
38246         "1",
38247         1,
38248         ["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"]
38249       ],
38250       [
38251         "Cape Verde (Kabu Verdi)",
38252         "cv",
38253         "238"
38254       ],
38255       [
38256         "Caribbean Netherlands",
38257         "bq",
38258         "599",
38259         1
38260       ],
38261       [
38262         "Cayman Islands",
38263         "ky",
38264         "1345"
38265       ],
38266       [
38267         "Central African Republic (République centrafricaine)",
38268         "cf",
38269         "236"
38270       ],
38271       [
38272         "Chad (Tchad)",
38273         "td",
38274         "235"
38275       ],
38276       [
38277         "Chile",
38278         "cl",
38279         "56"
38280       ],
38281       [
38282         "China (中国)",
38283         "cn",
38284         "86"
38285       ],
38286       [
38287         "Christmas Island",
38288         "cx",
38289         "61",
38290         2
38291       ],
38292       [
38293         "Cocos (Keeling) Islands",
38294         "cc",
38295         "61",
38296         1
38297       ],
38298       [
38299         "Colombia",
38300         "co",
38301         "57"
38302       ],
38303       [
38304         "Comoros (‫جزر القمر‬‎)",
38305         "km",
38306         "269"
38307       ],
38308       [
38309         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38310         "cd",
38311         "243"
38312       ],
38313       [
38314         "Congo (Republic) (Congo-Brazzaville)",
38315         "cg",
38316         "242"
38317       ],
38318       [
38319         "Cook Islands",
38320         "ck",
38321         "682"
38322       ],
38323       [
38324         "Costa Rica",
38325         "cr",
38326         "506"
38327       ],
38328       [
38329         "Côte d’Ivoire",
38330         "ci",
38331         "225"
38332       ],
38333       [
38334         "Croatia (Hrvatska)",
38335         "hr",
38336         "385"
38337       ],
38338       [
38339         "Cuba",
38340         "cu",
38341         "53"
38342       ],
38343       [
38344         "Curaçao",
38345         "cw",
38346         "599",
38347         0
38348       ],
38349       [
38350         "Cyprus (Κύπρος)",
38351         "cy",
38352         "357"
38353       ],
38354       [
38355         "Czech Republic (Česká republika)",
38356         "cz",
38357         "420"
38358       ],
38359       [
38360         "Denmark (Danmark)",
38361         "dk",
38362         "45"
38363       ],
38364       [
38365         "Djibouti",
38366         "dj",
38367         "253"
38368       ],
38369       [
38370         "Dominica",
38371         "dm",
38372         "1767"
38373       ],
38374       [
38375         "Dominican Republic (República Dominicana)",
38376         "do",
38377         "1",
38378         2,
38379         ["809", "829", "849"]
38380       ],
38381       [
38382         "Ecuador",
38383         "ec",
38384         "593"
38385       ],
38386       [
38387         "Egypt (‫مصر‬‎)",
38388         "eg",
38389         "20"
38390       ],
38391       [
38392         "El Salvador",
38393         "sv",
38394         "503"
38395       ],
38396       [
38397         "Equatorial Guinea (Guinea Ecuatorial)",
38398         "gq",
38399         "240"
38400       ],
38401       [
38402         "Eritrea",
38403         "er",
38404         "291"
38405       ],
38406       [
38407         "Estonia (Eesti)",
38408         "ee",
38409         "372"
38410       ],
38411       [
38412         "Ethiopia",
38413         "et",
38414         "251"
38415       ],
38416       [
38417         "Falkland Islands (Islas Malvinas)",
38418         "fk",
38419         "500"
38420       ],
38421       [
38422         "Faroe Islands (Føroyar)",
38423         "fo",
38424         "298"
38425       ],
38426       [
38427         "Fiji",
38428         "fj",
38429         "679"
38430       ],
38431       [
38432         "Finland (Suomi)",
38433         "fi",
38434         "358",
38435         0
38436       ],
38437       [
38438         "France",
38439         "fr",
38440         "33"
38441       ],
38442       [
38443         "French Guiana (Guyane française)",
38444         "gf",
38445         "594"
38446       ],
38447       [
38448         "French Polynesia (Polynésie française)",
38449         "pf",
38450         "689"
38451       ],
38452       [
38453         "Gabon",
38454         "ga",
38455         "241"
38456       ],
38457       [
38458         "Gambia",
38459         "gm",
38460         "220"
38461       ],
38462       [
38463         "Georgia (საქართველო)",
38464         "ge",
38465         "995"
38466       ],
38467       [
38468         "Germany (Deutschland)",
38469         "de",
38470         "49"
38471       ],
38472       [
38473         "Ghana (Gaana)",
38474         "gh",
38475         "233"
38476       ],
38477       [
38478         "Gibraltar",
38479         "gi",
38480         "350"
38481       ],
38482       [
38483         "Greece (Ελλάδα)",
38484         "gr",
38485         "30"
38486       ],
38487       [
38488         "Greenland (Kalaallit Nunaat)",
38489         "gl",
38490         "299"
38491       ],
38492       [
38493         "Grenada",
38494         "gd",
38495         "1473"
38496       ],
38497       [
38498         "Guadeloupe",
38499         "gp",
38500         "590",
38501         0
38502       ],
38503       [
38504         "Guam",
38505         "gu",
38506         "1671"
38507       ],
38508       [
38509         "Guatemala",
38510         "gt",
38511         "502"
38512       ],
38513       [
38514         "Guernsey",
38515         "gg",
38516         "44",
38517         1
38518       ],
38519       [
38520         "Guinea (Guinée)",
38521         "gn",
38522         "224"
38523       ],
38524       [
38525         "Guinea-Bissau (Guiné Bissau)",
38526         "gw",
38527         "245"
38528       ],
38529       [
38530         "Guyana",
38531         "gy",
38532         "592"
38533       ],
38534       [
38535         "Haiti",
38536         "ht",
38537         "509"
38538       ],
38539       [
38540         "Honduras",
38541         "hn",
38542         "504"
38543       ],
38544       [
38545         "Hong Kong (香港)",
38546         "hk",
38547         "852"
38548       ],
38549       [
38550         "Hungary (Magyarország)",
38551         "hu",
38552         "36"
38553       ],
38554       [
38555         "Iceland (Ísland)",
38556         "is",
38557         "354"
38558       ],
38559       [
38560         "India (भारत)",
38561         "in",
38562         "91"
38563       ],
38564       [
38565         "Indonesia",
38566         "id",
38567         "62"
38568       ],
38569       [
38570         "Iran (‫ایران‬‎)",
38571         "ir",
38572         "98"
38573       ],
38574       [
38575         "Iraq (‫العراق‬‎)",
38576         "iq",
38577         "964"
38578       ],
38579       [
38580         "Ireland",
38581         "ie",
38582         "353"
38583       ],
38584       [
38585         "Isle of Man",
38586         "im",
38587         "44",
38588         2
38589       ],
38590       [
38591         "Israel (‫ישראל‬‎)",
38592         "il",
38593         "972"
38594       ],
38595       [
38596         "Italy (Italia)",
38597         "it",
38598         "39",
38599         0
38600       ],
38601       [
38602         "Jamaica",
38603         "jm",
38604         "1876"
38605       ],
38606       [
38607         "Japan (日本)",
38608         "jp",
38609         "81"
38610       ],
38611       [
38612         "Jersey",
38613         "je",
38614         "44",
38615         3
38616       ],
38617       [
38618         "Jordan (‫الأردن‬‎)",
38619         "jo",
38620         "962"
38621       ],
38622       [
38623         "Kazakhstan (Казахстан)",
38624         "kz",
38625         "7",
38626         1
38627       ],
38628       [
38629         "Kenya",
38630         "ke",
38631         "254"
38632       ],
38633       [
38634         "Kiribati",
38635         "ki",
38636         "686"
38637       ],
38638       [
38639         "Kosovo",
38640         "xk",
38641         "383"
38642       ],
38643       [
38644         "Kuwait (‫الكويت‬‎)",
38645         "kw",
38646         "965"
38647       ],
38648       [
38649         "Kyrgyzstan (Кыргызстан)",
38650         "kg",
38651         "996"
38652       ],
38653       [
38654         "Laos (ລາວ)",
38655         "la",
38656         "856"
38657       ],
38658       [
38659         "Latvia (Latvija)",
38660         "lv",
38661         "371"
38662       ],
38663       [
38664         "Lebanon (‫لبنان‬‎)",
38665         "lb",
38666         "961"
38667       ],
38668       [
38669         "Lesotho",
38670         "ls",
38671         "266"
38672       ],
38673       [
38674         "Liberia",
38675         "lr",
38676         "231"
38677       ],
38678       [
38679         "Libya (‫ليبيا‬‎)",
38680         "ly",
38681         "218"
38682       ],
38683       [
38684         "Liechtenstein",
38685         "li",
38686         "423"
38687       ],
38688       [
38689         "Lithuania (Lietuva)",
38690         "lt",
38691         "370"
38692       ],
38693       [
38694         "Luxembourg",
38695         "lu",
38696         "352"
38697       ],
38698       [
38699         "Macau (澳門)",
38700         "mo",
38701         "853"
38702       ],
38703       [
38704         "Macedonia (FYROM) (Македонија)",
38705         "mk",
38706         "389"
38707       ],
38708       [
38709         "Madagascar (Madagasikara)",
38710         "mg",
38711         "261"
38712       ],
38713       [
38714         "Malawi",
38715         "mw",
38716         "265"
38717       ],
38718       [
38719         "Malaysia",
38720         "my",
38721         "60"
38722       ],
38723       [
38724         "Maldives",
38725         "mv",
38726         "960"
38727       ],
38728       [
38729         "Mali",
38730         "ml",
38731         "223"
38732       ],
38733       [
38734         "Malta",
38735         "mt",
38736         "356"
38737       ],
38738       [
38739         "Marshall Islands",
38740         "mh",
38741         "692"
38742       ],
38743       [
38744         "Martinique",
38745         "mq",
38746         "596"
38747       ],
38748       [
38749         "Mauritania (‫موريتانيا‬‎)",
38750         "mr",
38751         "222"
38752       ],
38753       [
38754         "Mauritius (Moris)",
38755         "mu",
38756         "230"
38757       ],
38758       [
38759         "Mayotte",
38760         "yt",
38761         "262",
38762         1
38763       ],
38764       [
38765         "Mexico (México)",
38766         "mx",
38767         "52"
38768       ],
38769       [
38770         "Micronesia",
38771         "fm",
38772         "691"
38773       ],
38774       [
38775         "Moldova (Republica Moldova)",
38776         "md",
38777         "373"
38778       ],
38779       [
38780         "Monaco",
38781         "mc",
38782         "377"
38783       ],
38784       [
38785         "Mongolia (Монгол)",
38786         "mn",
38787         "976"
38788       ],
38789       [
38790         "Montenegro (Crna Gora)",
38791         "me",
38792         "382"
38793       ],
38794       [
38795         "Montserrat",
38796         "ms",
38797         "1664"
38798       ],
38799       [
38800         "Morocco (‫المغرب‬‎)",
38801         "ma",
38802         "212",
38803         0
38804       ],
38805       [
38806         "Mozambique (Moçambique)",
38807         "mz",
38808         "258"
38809       ],
38810       [
38811         "Myanmar (Burma) (မြန်မာ)",
38812         "mm",
38813         "95"
38814       ],
38815       [
38816         "Namibia (Namibië)",
38817         "na",
38818         "264"
38819       ],
38820       [
38821         "Nauru",
38822         "nr",
38823         "674"
38824       ],
38825       [
38826         "Nepal (नेपाल)",
38827         "np",
38828         "977"
38829       ],
38830       [
38831         "Netherlands (Nederland)",
38832         "nl",
38833         "31"
38834       ],
38835       [
38836         "New Caledonia (Nouvelle-Calédonie)",
38837         "nc",
38838         "687"
38839       ],
38840       [
38841         "New Zealand",
38842         "nz",
38843         "64"
38844       ],
38845       [
38846         "Nicaragua",
38847         "ni",
38848         "505"
38849       ],
38850       [
38851         "Niger (Nijar)",
38852         "ne",
38853         "227"
38854       ],
38855       [
38856         "Nigeria",
38857         "ng",
38858         "234"
38859       ],
38860       [
38861         "Niue",
38862         "nu",
38863         "683"
38864       ],
38865       [
38866         "Norfolk Island",
38867         "nf",
38868         "672"
38869       ],
38870       [
38871         "North Korea (조선 민주주의 인민 공화국)",
38872         "kp",
38873         "850"
38874       ],
38875       [
38876         "Northern Mariana Islands",
38877         "mp",
38878         "1670"
38879       ],
38880       [
38881         "Norway (Norge)",
38882         "no",
38883         "47",
38884         0
38885       ],
38886       [
38887         "Oman (‫عُمان‬‎)",
38888         "om",
38889         "968"
38890       ],
38891       [
38892         "Pakistan (‫پاکستان‬‎)",
38893         "pk",
38894         "92"
38895       ],
38896       [
38897         "Palau",
38898         "pw",
38899         "680"
38900       ],
38901       [
38902         "Palestine (‫فلسطين‬‎)",
38903         "ps",
38904         "970"
38905       ],
38906       [
38907         "Panama (Panamá)",
38908         "pa",
38909         "507"
38910       ],
38911       [
38912         "Papua New Guinea",
38913         "pg",
38914         "675"
38915       ],
38916       [
38917         "Paraguay",
38918         "py",
38919         "595"
38920       ],
38921       [
38922         "Peru (Perú)",
38923         "pe",
38924         "51"
38925       ],
38926       [
38927         "Philippines",
38928         "ph",
38929         "63"
38930       ],
38931       [
38932         "Poland (Polska)",
38933         "pl",
38934         "48"
38935       ],
38936       [
38937         "Portugal",
38938         "pt",
38939         "351"
38940       ],
38941       [
38942         "Puerto Rico",
38943         "pr",
38944         "1",
38945         3,
38946         ["787", "939"]
38947       ],
38948       [
38949         "Qatar (‫قطر‬‎)",
38950         "qa",
38951         "974"
38952       ],
38953       [
38954         "Réunion (La Réunion)",
38955         "re",
38956         "262",
38957         0
38958       ],
38959       [
38960         "Romania (România)",
38961         "ro",
38962         "40"
38963       ],
38964       [
38965         "Russia (Россия)",
38966         "ru",
38967         "7",
38968         0
38969       ],
38970       [
38971         "Rwanda",
38972         "rw",
38973         "250"
38974       ],
38975       [
38976         "Saint Barthélemy",
38977         "bl",
38978         "590",
38979         1
38980       ],
38981       [
38982         "Saint Helena",
38983         "sh",
38984         "290"
38985       ],
38986       [
38987         "Saint Kitts and Nevis",
38988         "kn",
38989         "1869"
38990       ],
38991       [
38992         "Saint Lucia",
38993         "lc",
38994         "1758"
38995       ],
38996       [
38997         "Saint Martin (Saint-Martin (partie française))",
38998         "mf",
38999         "590",
39000         2
39001       ],
39002       [
39003         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39004         "pm",
39005         "508"
39006       ],
39007       [
39008         "Saint Vincent and the Grenadines",
39009         "vc",
39010         "1784"
39011       ],
39012       [
39013         "Samoa",
39014         "ws",
39015         "685"
39016       ],
39017       [
39018         "San Marino",
39019         "sm",
39020         "378"
39021       ],
39022       [
39023         "São Tomé and Príncipe (São Tomé e Príncipe)",
39024         "st",
39025         "239"
39026       ],
39027       [
39028         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39029         "sa",
39030         "966"
39031       ],
39032       [
39033         "Senegal (Sénégal)",
39034         "sn",
39035         "221"
39036       ],
39037       [
39038         "Serbia (Србија)",
39039         "rs",
39040         "381"
39041       ],
39042       [
39043         "Seychelles",
39044         "sc",
39045         "248"
39046       ],
39047       [
39048         "Sierra Leone",
39049         "sl",
39050         "232"
39051       ],
39052       [
39053         "Singapore",
39054         "sg",
39055         "65"
39056       ],
39057       [
39058         "Sint Maarten",
39059         "sx",
39060         "1721"
39061       ],
39062       [
39063         "Slovakia (Slovensko)",
39064         "sk",
39065         "421"
39066       ],
39067       [
39068         "Slovenia (Slovenija)",
39069         "si",
39070         "386"
39071       ],
39072       [
39073         "Solomon Islands",
39074         "sb",
39075         "677"
39076       ],
39077       [
39078         "Somalia (Soomaaliya)",
39079         "so",
39080         "252"
39081       ],
39082       [
39083         "South Africa",
39084         "za",
39085         "27"
39086       ],
39087       [
39088         "South Korea (대한민국)",
39089         "kr",
39090         "82"
39091       ],
39092       [
39093         "South Sudan (‫جنوب السودان‬‎)",
39094         "ss",
39095         "211"
39096       ],
39097       [
39098         "Spain (España)",
39099         "es",
39100         "34"
39101       ],
39102       [
39103         "Sri Lanka (ශ්‍රී ලංකාව)",
39104         "lk",
39105         "94"
39106       ],
39107       [
39108         "Sudan (‫السودان‬‎)",
39109         "sd",
39110         "249"
39111       ],
39112       [
39113         "Suriname",
39114         "sr",
39115         "597"
39116       ],
39117       [
39118         "Svalbard and Jan Mayen",
39119         "sj",
39120         "47",
39121         1
39122       ],
39123       [
39124         "Swaziland",
39125         "sz",
39126         "268"
39127       ],
39128       [
39129         "Sweden (Sverige)",
39130         "se",
39131         "46"
39132       ],
39133       [
39134         "Switzerland (Schweiz)",
39135         "ch",
39136         "41"
39137       ],
39138       [
39139         "Syria (‫سوريا‬‎)",
39140         "sy",
39141         "963"
39142       ],
39143       [
39144         "Taiwan (台灣)",
39145         "tw",
39146         "886"
39147       ],
39148       [
39149         "Tajikistan",
39150         "tj",
39151         "992"
39152       ],
39153       [
39154         "Tanzania",
39155         "tz",
39156         "255"
39157       ],
39158       [
39159         "Thailand (ไทย)",
39160         "th",
39161         "66"
39162       ],
39163       [
39164         "Timor-Leste",
39165         "tl",
39166         "670"
39167       ],
39168       [
39169         "Togo",
39170         "tg",
39171         "228"
39172       ],
39173       [
39174         "Tokelau",
39175         "tk",
39176         "690"
39177       ],
39178       [
39179         "Tonga",
39180         "to",
39181         "676"
39182       ],
39183       [
39184         "Trinidad and Tobago",
39185         "tt",
39186         "1868"
39187       ],
39188       [
39189         "Tunisia (‫تونس‬‎)",
39190         "tn",
39191         "216"
39192       ],
39193       [
39194         "Turkey (Türkiye)",
39195         "tr",
39196         "90"
39197       ],
39198       [
39199         "Turkmenistan",
39200         "tm",
39201         "993"
39202       ],
39203       [
39204         "Turks and Caicos Islands",
39205         "tc",
39206         "1649"
39207       ],
39208       [
39209         "Tuvalu",
39210         "tv",
39211         "688"
39212       ],
39213       [
39214         "U.S. Virgin Islands",
39215         "vi",
39216         "1340"
39217       ],
39218       [
39219         "Uganda",
39220         "ug",
39221         "256"
39222       ],
39223       [
39224         "Ukraine (Україна)",
39225         "ua",
39226         "380"
39227       ],
39228       [
39229         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39230         "ae",
39231         "971"
39232       ],
39233       [
39234         "United Kingdom",
39235         "gb",
39236         "44",
39237         0
39238       ],
39239       [
39240         "United States",
39241         "us",
39242         "1",
39243         0
39244       ],
39245       [
39246         "Uruguay",
39247         "uy",
39248         "598"
39249       ],
39250       [
39251         "Uzbekistan (Oʻzbekiston)",
39252         "uz",
39253         "998"
39254       ],
39255       [
39256         "Vanuatu",
39257         "vu",
39258         "678"
39259       ],
39260       [
39261         "Vatican City (Città del Vaticano)",
39262         "va",
39263         "39",
39264         1
39265       ],
39266       [
39267         "Venezuela",
39268         "ve",
39269         "58"
39270       ],
39271       [
39272         "Vietnam (Việt Nam)",
39273         "vn",
39274         "84"
39275       ],
39276       [
39277         "Wallis and Futuna (Wallis-et-Futuna)",
39278         "wf",
39279         "681"
39280       ],
39281       [
39282         "Western Sahara (‫الصحراء الغربية‬‎)",
39283         "eh",
39284         "212",
39285         1
39286       ],
39287       [
39288         "Yemen (‫اليمن‬‎)",
39289         "ye",
39290         "967"
39291       ],
39292       [
39293         "Zambia",
39294         "zm",
39295         "260"
39296       ],
39297       [
39298         "Zimbabwe",
39299         "zw",
39300         "263"
39301       ],
39302       [
39303         "Åland Islands",
39304         "ax",
39305         "358",
39306         1
39307       ]
39308   ];
39309   
39310   return d;
39311 }/**
39312 *    This script refer to:
39313 *    Title: International Telephone Input
39314 *    Author: Jack O'Connor
39315 *    Code version:  v12.1.12
39316 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39317 **/
39318
39319 /**
39320  * @class Roo.bootstrap.PhoneInput
39321  * @extends Roo.bootstrap.TriggerField
39322  * An input with International dial-code selection
39323  
39324  * @cfg {String} defaultDialCode default '+852'
39325  * @cfg {Array} preferedCountries default []
39326   
39327  * @constructor
39328  * Create a new PhoneInput.
39329  * @param {Object} config Configuration options
39330  */
39331
39332 Roo.bootstrap.PhoneInput = function(config) {
39333     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39334 };
39335
39336 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39337         
39338         listWidth: undefined,
39339         
39340         selectedClass: 'active',
39341         
39342         invalidClass : "has-warning",
39343         
39344         validClass: 'has-success',
39345         
39346         allowed: '0123456789',
39347         
39348         /**
39349          * @cfg {String} defaultDialCode The default dial code when initializing the input
39350          */
39351         defaultDialCode: '+852',
39352         
39353         /**
39354          * @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
39355          */
39356         preferedCountries: false,
39357         
39358         getAutoCreate : function()
39359         {
39360             var data = Roo.bootstrap.PhoneInputData();
39361             var align = this.labelAlign || this.parentLabelAlign();
39362             var id = Roo.id();
39363             
39364             this.allCountries = [];
39365             this.dialCodeMapping = [];
39366             
39367             for (var i = 0; i < data.length; i++) {
39368               var c = data[i];
39369               this.allCountries[i] = {
39370                 name: c[0],
39371                 iso2: c[1],
39372                 dialCode: c[2],
39373                 priority: c[3] || 0,
39374                 areaCodes: c[4] || null
39375               };
39376               this.dialCodeMapping[c[2]] = {
39377                   name: c[0],
39378                   iso2: c[1],
39379                   priority: c[3] || 0,
39380                   areaCodes: c[4] || null
39381               };
39382             }
39383             
39384             var cfg = {
39385                 cls: 'form-group',
39386                 cn: []
39387             };
39388             
39389             var input =  {
39390                 tag: 'input',
39391                 id : id,
39392                 cls : 'form-control tel-input',
39393                 autocomplete: 'new-password'
39394             };
39395             
39396             var hiddenInput = {
39397                 tag: 'input',
39398                 type: 'hidden',
39399                 cls: 'hidden-tel-input'
39400             };
39401             
39402             if (this.name) {
39403                 hiddenInput.name = this.name;
39404             }
39405             
39406             if (this.disabled) {
39407                 input.disabled = true;
39408             }
39409             
39410             var flag_container = {
39411                 tag: 'div',
39412                 cls: 'flag-box',
39413                 cn: [
39414                     {
39415                         tag: 'div',
39416                         cls: 'flag'
39417                     },
39418                     {
39419                         tag: 'div',
39420                         cls: 'caret'
39421                     }
39422                 ]
39423             };
39424             
39425             var box = {
39426                 tag: 'div',
39427                 cls: this.hasFeedback ? 'has-feedback' : '',
39428                 cn: [
39429                     hiddenInput,
39430                     input,
39431                     {
39432                         tag: 'input',
39433                         cls: 'dial-code-holder',
39434                         disabled: true
39435                     }
39436                 ]
39437             };
39438             
39439             var container = {
39440                 cls: 'roo-select2-container input-group',
39441                 cn: [
39442                     flag_container,
39443                     box
39444                 ]
39445             };
39446             
39447             if (this.fieldLabel.length) {
39448                 var indicator = {
39449                     tag: 'i',
39450                     tooltip: 'This field is required'
39451                 };
39452                 
39453                 var label = {
39454                     tag: 'label',
39455                     'for':  id,
39456                     cls: 'control-label',
39457                     cn: []
39458                 };
39459                 
39460                 var label_text = {
39461                     tag: 'span',
39462                     html: this.fieldLabel
39463                 };
39464                 
39465                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39466                 label.cn = [
39467                     indicator,
39468                     label_text
39469                 ];
39470                 
39471                 if(this.indicatorpos == 'right') {
39472                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39473                     label.cn = [
39474                         label_text,
39475                         indicator
39476                     ];
39477                 }
39478                 
39479                 if(align == 'left') {
39480                     container = {
39481                         tag: 'div',
39482                         cn: [
39483                             container
39484                         ]
39485                     };
39486                     
39487                     if(this.labelWidth > 12){
39488                         label.style = "width: " + this.labelWidth + 'px';
39489                     }
39490                     if(this.labelWidth < 13 && this.labelmd == 0){
39491                         this.labelmd = this.labelWidth;
39492                     }
39493                     if(this.labellg > 0){
39494                         label.cls += ' col-lg-' + this.labellg;
39495                         input.cls += ' col-lg-' + (12 - this.labellg);
39496                     }
39497                     if(this.labelmd > 0){
39498                         label.cls += ' col-md-' + this.labelmd;
39499                         container.cls += ' col-md-' + (12 - this.labelmd);
39500                     }
39501                     if(this.labelsm > 0){
39502                         label.cls += ' col-sm-' + this.labelsm;
39503                         container.cls += ' col-sm-' + (12 - this.labelsm);
39504                     }
39505                     if(this.labelxs > 0){
39506                         label.cls += ' col-xs-' + this.labelxs;
39507                         container.cls += ' col-xs-' + (12 - this.labelxs);
39508                     }
39509                 }
39510             }
39511             
39512             cfg.cn = [
39513                 label,
39514                 container
39515             ];
39516             
39517             var settings = this;
39518             
39519             ['xs','sm','md','lg'].map(function(size){
39520                 if (settings[size]) {
39521                     cfg.cls += ' col-' + size + '-' + settings[size];
39522                 }
39523             });
39524             
39525             this.store = new Roo.data.Store({
39526                 proxy : new Roo.data.MemoryProxy({}),
39527                 reader : new Roo.data.JsonReader({
39528                     fields : [
39529                         {
39530                             'name' : 'name',
39531                             'type' : 'string'
39532                         },
39533                         {
39534                             'name' : 'iso2',
39535                             'type' : 'string'
39536                         },
39537                         {
39538                             'name' : 'dialCode',
39539                             'type' : 'string'
39540                         },
39541                         {
39542                             'name' : 'priority',
39543                             'type' : 'string'
39544                         },
39545                         {
39546                             'name' : 'areaCodes',
39547                             'type' : 'string'
39548                         }
39549                     ]
39550                 })
39551             });
39552             
39553             if(!this.preferedCountries) {
39554                 this.preferedCountries = [
39555                     'hk',
39556                     'gb',
39557                     'us'
39558                 ];
39559             }
39560             
39561             var p = this.preferedCountries.reverse();
39562             
39563             if(p) {
39564                 for (var i = 0; i < p.length; i++) {
39565                     for (var j = 0; j < this.allCountries.length; j++) {
39566                         if(this.allCountries[j].iso2 == p[i]) {
39567                             var t = this.allCountries[j];
39568                             this.allCountries.splice(j,1);
39569                             this.allCountries.unshift(t);
39570                         }
39571                     } 
39572                 }
39573             }
39574             
39575             this.store.proxy.data = {
39576                 success: true,
39577                 data: this.allCountries
39578             };
39579             
39580             return cfg;
39581         },
39582         
39583         initEvents : function()
39584         {
39585             this.createList();
39586             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39587             
39588             this.indicator = this.indicatorEl();
39589             this.flag = this.flagEl();
39590             this.dialCodeHolder = this.dialCodeHolderEl();
39591             
39592             this.trigger = this.el.select('div.flag-box',true).first();
39593             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39594             
39595             var _this = this;
39596             
39597             (function(){
39598                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39599                 _this.list.setWidth(lw);
39600             }).defer(100);
39601             
39602             this.list.on('mouseover', this.onViewOver, this);
39603             this.list.on('mousemove', this.onViewMove, this);
39604             this.inputEl().on("keyup", this.onKeyUp, this);
39605             
39606             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39607
39608             this.view = new Roo.View(this.list, this.tpl, {
39609                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39610             });
39611             
39612             this.view.on('click', this.onViewClick, this);
39613             this.setValue(this.defaultDialCode);
39614         },
39615         
39616         onTriggerClick : function(e)
39617         {
39618             Roo.log('trigger click');
39619             if(this.disabled){
39620                 return;
39621             }
39622             
39623             if(this.isExpanded()){
39624                 this.collapse();
39625                 this.hasFocus = false;
39626             }else {
39627                 this.store.load({});
39628                 this.hasFocus = true;
39629                 this.expand();
39630             }
39631         },
39632         
39633         isExpanded : function()
39634         {
39635             return this.list.isVisible();
39636         },
39637         
39638         collapse : function()
39639         {
39640             if(!this.isExpanded()){
39641                 return;
39642             }
39643             this.list.hide();
39644             Roo.get(document).un('mousedown', this.collapseIf, this);
39645             Roo.get(document).un('mousewheel', this.collapseIf, this);
39646             this.fireEvent('collapse', this);
39647             this.validate();
39648         },
39649         
39650         expand : function()
39651         {
39652             Roo.log('expand');
39653
39654             if(this.isExpanded() || !this.hasFocus){
39655                 return;
39656             }
39657             
39658             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39659             this.list.setWidth(lw);
39660             
39661             this.list.show();
39662             this.restrictHeight();
39663             
39664             Roo.get(document).on('mousedown', this.collapseIf, this);
39665             Roo.get(document).on('mousewheel', this.collapseIf, this);
39666             
39667             this.fireEvent('expand', this);
39668         },
39669         
39670         restrictHeight : function()
39671         {
39672             this.list.alignTo(this.inputEl(), this.listAlign);
39673             this.list.alignTo(this.inputEl(), this.listAlign);
39674         },
39675         
39676         onViewOver : function(e, t)
39677         {
39678             if(this.inKeyMode){
39679                 return;
39680             }
39681             var item = this.view.findItemFromChild(t);
39682             
39683             if(item){
39684                 var index = this.view.indexOf(item);
39685                 this.select(index, false);
39686             }
39687         },
39688
39689         // private
39690         onViewClick : function(view, doFocus, el, e)
39691         {
39692             var index = this.view.getSelectedIndexes()[0];
39693             
39694             var r = this.store.getAt(index);
39695             
39696             if(r){
39697                 this.onSelect(r, index);
39698             }
39699             if(doFocus !== false && !this.blockFocus){
39700                 this.inputEl().focus();
39701             }
39702         },
39703         
39704         onViewMove : function(e, t)
39705         {
39706             this.inKeyMode = false;
39707         },
39708         
39709         select : function(index, scrollIntoView)
39710         {
39711             this.selectedIndex = index;
39712             this.view.select(index);
39713             if(scrollIntoView !== false){
39714                 var el = this.view.getNode(index);
39715                 if(el){
39716                     this.list.scrollChildIntoView(el, false);
39717                 }
39718             }
39719         },
39720         
39721         createList : function()
39722         {
39723             this.list = Roo.get(document.body).createChild({
39724                 tag: 'ul',
39725                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39726                 style: 'display:none'
39727             });
39728             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39729         },
39730         
39731         collapseIf : function(e)
39732         {
39733             var in_combo  = e.within(this.el);
39734             var in_list =  e.within(this.list);
39735             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39736             
39737             if (in_combo || in_list || is_list) {
39738                 return;
39739             }
39740             this.collapse();
39741         },
39742         
39743         onSelect : function(record, index)
39744         {
39745             if(this.fireEvent('beforeselect', this, record, index) !== false){
39746                 
39747                 this.setFlagClass(record.data.iso2);
39748                 this.setDialCode(record.data.dialCode);
39749                 this.hasFocus = false;
39750                 this.collapse();
39751                 this.fireEvent('select', this, record, index);
39752             }
39753         },
39754         
39755         flagEl : function()
39756         {
39757             var flag = this.el.select('div.flag',true).first();
39758             if(!flag){
39759                 return false;
39760             }
39761             return flag;
39762         },
39763         
39764         dialCodeHolderEl : function()
39765         {
39766             var d = this.el.select('input.dial-code-holder',true).first();
39767             if(!d){
39768                 return false;
39769             }
39770             return d;
39771         },
39772         
39773         setDialCode : function(v)
39774         {
39775             this.dialCodeHolder.dom.value = '+'+v;
39776         },
39777         
39778         setFlagClass : function(n)
39779         {
39780             this.flag.dom.className = 'flag '+n;
39781         },
39782         
39783         getValue : function()
39784         {
39785             var v = this.inputEl().getValue();
39786             if(this.dialCodeHolder) {
39787                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39788             }
39789             return v;
39790         },
39791         
39792         setValue : function(v)
39793         {
39794             var d = this.getDialCode(v);
39795             
39796             //invalid dial code
39797             if(v.length == 0 || !d || d.length == 0) {
39798                 if(this.rendered){
39799                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39800                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39801                 }
39802                 return;
39803             }
39804             
39805             //valid dial code
39806             this.setFlagClass(this.dialCodeMapping[d].iso2);
39807             this.setDialCode(d);
39808             this.inputEl().dom.value = v.replace('+'+d,'');
39809             this.hiddenEl().dom.value = this.getValue();
39810             
39811             this.validate();
39812         },
39813         
39814         getDialCode : function(v = '')
39815         {
39816             if (v.length == 0) {
39817                 return this.dialCodeHolder.dom.value;
39818             }
39819             
39820             var dialCode = "";
39821             if (v.charAt(0) != "+") {
39822                 return false;
39823             }
39824             var numericChars = "";
39825             for (var i = 1; i < v.length; i++) {
39826               var c = v.charAt(i);
39827               if (!isNaN(c)) {
39828                 numericChars += c;
39829                 if (this.dialCodeMapping[numericChars]) {
39830                   dialCode = v.substr(1, i);
39831                 }
39832                 if (numericChars.length == 4) {
39833                   break;
39834                 }
39835               }
39836             }
39837             return dialCode;
39838         },
39839         
39840         reset : function()
39841         {
39842             this.setValue(this.defaultDialCode);
39843             this.validate();
39844         },
39845         
39846         hiddenEl : function()
39847         {
39848             return this.el.select('input.hidden-tel-input',true).first();
39849         },
39850         
39851         onKeyUp : function(e){
39852             
39853             var k = e.getKey();
39854             var c = e.getCharCode();
39855             
39856             if(
39857                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39858                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39859             ){
39860                 e.stopEvent();
39861             }
39862             
39863             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39864             //     return;
39865             // }
39866             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39867                 e.stopEvent();
39868             }
39869             
39870             this.setValue(this.getValue());
39871         }
39872         
39873 });
39874 /**
39875  * @class Roo.bootstrap.MoneyField
39876  * @extends Roo.bootstrap.ComboBox
39877  * Bootstrap MoneyField class
39878  * 
39879  * @constructor
39880  * Create a new MoneyField.
39881  * @param {Object} config Configuration options
39882  */
39883
39884 Roo.bootstrap.MoneyField = function(config) {
39885     
39886     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39887     
39888 };
39889
39890 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39891     
39892     /**
39893      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39894      */
39895     allowDecimals : true,
39896     /**
39897      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39898      */
39899     decimalSeparator : ".",
39900     /**
39901      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39902      */
39903     decimalPrecision : 2,
39904     /**
39905      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39906      */
39907     allowNegative : true,
39908     /**
39909      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39910      */
39911     minValue : Number.NEGATIVE_INFINITY,
39912     /**
39913      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39914      */
39915     maxValue : Number.MAX_VALUE,
39916     /**
39917      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39918      */
39919     minText : "The minimum value for this field is {0}",
39920     /**
39921      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39922      */
39923     maxText : "The maximum value for this field is {0}",
39924     /**
39925      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39926      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39927      */
39928     nanText : "{0} is not a valid number",
39929     /**
39930      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39931      */
39932     castInt : true,
39933     
39934     inputlg : 9,
39935     inputmd : 9,
39936     inputsm : 9,
39937     inputxs : 6,
39938     
39939     store : false,
39940     
39941     getAutoCreate : function()
39942     {
39943         var align = this.labelAlign || this.parentLabelAlign();
39944         
39945         var id = Roo.id();
39946
39947         var cfg = {
39948             cls: 'form-group',
39949             cn: []
39950         };
39951
39952         var input =  {
39953             tag: 'input',
39954             id : id,
39955             cls : 'form-control roo-money-amount-input',
39956             autocomplete: 'new-password'
39957         };
39958         
39959         if (this.name) {
39960             input.name = this.name;
39961         }
39962
39963         if (this.disabled) {
39964             input.disabled = true;
39965         }
39966
39967         var clg = 12 - this.inputlg;
39968         var cmd = 12 - this.inputmd;
39969         var csm = 12 - this.inputsm;
39970         var cxs = 12 - this.inputxs;
39971         
39972         var container = {
39973             tag : 'div',
39974             cls : 'row roo-money-field',
39975             cn : [
39976                 {
39977                     tag : 'div',
39978                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39979                     cn : [
39980                         {
39981                             tag : 'div',
39982                             cls: 'roo-select2-container input-group',
39983                             cn: [
39984                                 {
39985                                     tag : 'input',
39986                                     cls : 'form-control roo-money-currency-input',
39987                                     autocomplete: 'new-password',
39988                                     readOnly : 1,
39989                                     name : this.currencyName
39990                                 },
39991                                 {
39992                                     tag :'span',
39993                                     cls : 'input-group-addon',
39994                                     cn : [
39995                                         {
39996                                             tag: 'span',
39997                                             cls: 'caret'
39998                                         }
39999                                     ]
40000                                 }
40001                             ]
40002                         }
40003                     ]
40004                 },
40005                 {
40006                     tag : 'div',
40007                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40008                     cn : [
40009                         {
40010                             tag: 'div',
40011                             cls: this.hasFeedback ? 'has-feedback' : '',
40012                             cn: [
40013                                 input
40014                             ]
40015                         }
40016                     ]
40017                 }
40018             ]
40019             
40020         };
40021         
40022         if (this.fieldLabel.length) {
40023             var indicator = {
40024                 tag: 'i',
40025                 tooltip: 'This field is required'
40026             };
40027
40028             var label = {
40029                 tag: 'label',
40030                 'for':  id,
40031                 cls: 'control-label',
40032                 cn: []
40033             };
40034
40035             var label_text = {
40036                 tag: 'span',
40037                 html: this.fieldLabel
40038             };
40039
40040             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40041             label.cn = [
40042                 indicator,
40043                 label_text
40044             ];
40045
40046             if(this.indicatorpos == 'right') {
40047                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40048                 label.cn = [
40049                     label_text,
40050                     indicator
40051                 ];
40052             }
40053
40054             if(align == 'left') {
40055                 container = {
40056                     tag: 'div',
40057                     cn: [
40058                         container
40059                     ]
40060                 };
40061
40062                 if(this.labelWidth > 12){
40063                     label.style = "width: " + this.labelWidth + 'px';
40064                 }
40065                 if(this.labelWidth < 13 && this.labelmd == 0){
40066                     this.labelmd = this.labelWidth;
40067                 }
40068                 if(this.labellg > 0){
40069                     label.cls += ' col-lg-' + this.labellg;
40070                     input.cls += ' col-lg-' + (12 - this.labellg);
40071                 }
40072                 if(this.labelmd > 0){
40073                     label.cls += ' col-md-' + this.labelmd;
40074                     container.cls += ' col-md-' + (12 - this.labelmd);
40075                 }
40076                 if(this.labelsm > 0){
40077                     label.cls += ' col-sm-' + this.labelsm;
40078                     container.cls += ' col-sm-' + (12 - this.labelsm);
40079                 }
40080                 if(this.labelxs > 0){
40081                     label.cls += ' col-xs-' + this.labelxs;
40082                     container.cls += ' col-xs-' + (12 - this.labelxs);
40083                 }
40084             }
40085         }
40086
40087         cfg.cn = [
40088             label,
40089             container
40090         ];
40091
40092         var settings = this;
40093
40094         ['xs','sm','md','lg'].map(function(size){
40095             if (settings[size]) {
40096                 cfg.cls += ' col-' + size + '-' + settings[size];
40097             }
40098         });
40099         
40100         return cfg;
40101         
40102     },
40103     
40104     initEvents : function()
40105     {
40106         this.indicator = this.indicatorEl();
40107         
40108         this.initCurrencyEvent();
40109         
40110         this.initNumberEvent();
40111         
40112     },
40113     
40114     initCurrencyEvent : function()
40115     {
40116         if (!this.store) {
40117             throw "can not find store for combo";
40118         }
40119         
40120         this.store = Roo.factory(this.store, Roo.data);
40121         this.store.parent = this;
40122         
40123         this.createList();
40124         
40125         this.triggerEl = this.el.select('.input-group-addon', true).first();
40126         
40127         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40128         
40129         var _this = this;
40130         
40131         (function(){
40132             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40133             _this.list.setWidth(lw);
40134         }).defer(100);
40135         
40136         this.list.on('mouseover', this.onViewOver, this);
40137         this.list.on('mousemove', this.onViewMove, this);
40138         this.list.on('scroll', this.onViewScroll, this);
40139         
40140         if(!this.tpl){
40141             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40142         }
40143         
40144         this.view = new Roo.View(this.list, this.tpl, {
40145             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40146         });
40147         
40148         this.view.on('click', this.onViewClick, this);
40149         
40150         this.store.on('beforeload', this.onBeforeLoad, this);
40151         this.store.on('load', this.onLoad, this);
40152         this.store.on('loadexception', this.onLoadException, this);
40153         
40154         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40155             "up" : function(e){
40156                 this.inKeyMode = true;
40157                 this.selectPrev();
40158             },
40159
40160             "down" : function(e){
40161                 if(!this.isExpanded()){
40162                     this.onTriggerClick();
40163                 }else{
40164                     this.inKeyMode = true;
40165                     this.selectNext();
40166                 }
40167             },
40168
40169             "enter" : function(e){
40170                 this.collapse();
40171                 
40172                 if(this.fireEvent("specialkey", this, e)){
40173                     this.onViewClick(false);
40174                 }
40175                 
40176                 return true;
40177             },
40178
40179             "esc" : function(e){
40180                 this.collapse();
40181             },
40182
40183             "tab" : function(e){
40184                 this.collapse();
40185                 
40186                 if(this.fireEvent("specialkey", this, e)){
40187                     this.onViewClick(false);
40188                 }
40189                 
40190                 return true;
40191             },
40192
40193             scope : this,
40194
40195             doRelay : function(foo, bar, hname){
40196                 if(hname == 'down' || this.scope.isExpanded()){
40197                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40198                 }
40199                 return true;
40200             },
40201
40202             forceKeyDown: true
40203         });
40204         
40205         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40206         
40207     },
40208     
40209     initNumberEvent : function(e)
40210     {
40211         this.inputEl().on("keydown" , this.fireKey,  this);
40212         this.inputEl().on("focus", this.onFocus,  this);
40213         this.inputEl().on("blur", this.onBlur,  this);
40214         
40215         this.inputEl().relayEvent('keyup', this);
40216         
40217         if(this.indicator){
40218             this.indicator.addClass('invisible');
40219         }
40220  
40221         this.originalValue = this.getValue();
40222         
40223         if(this.validationEvent == 'keyup'){
40224             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40225             this.inputEl().on('keyup', this.filterValidation, this);
40226         }
40227         else if(this.validationEvent !== false){
40228             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40229         }
40230         
40231         if(this.selectOnFocus){
40232             this.on("focus", this.preFocus, this);
40233             
40234         }
40235         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40236             this.inputEl().on("keypress", this.filterKeys, this);
40237         } else {
40238             this.inputEl().relayEvent('keypress', this);
40239         }
40240         
40241         var allowed = "0123456789";
40242         
40243         if(this.allowDecimals){
40244             allowed += this.decimalSeparator;
40245         }
40246         
40247         if(this.allowNegative){
40248             allowed += "-";
40249         }
40250         
40251         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40252         
40253         var keyPress = function(e){
40254             
40255             var k = e.getKey();
40256             
40257             var c = e.getCharCode();
40258             
40259             if(
40260                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40261                     allowed.indexOf(String.fromCharCode(c)) === -1
40262             ){
40263                 e.stopEvent();
40264                 return;
40265             }
40266             
40267             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40268                 return;
40269             }
40270             
40271             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40272                 e.stopEvent();
40273             }
40274         };
40275         
40276         this.inputEl().on("keypress", keyPress, this);
40277         
40278     },
40279     
40280     onTriggerClick : function(e)
40281     {   
40282         if(this.disabled){
40283             return;
40284         }
40285         
40286         this.page = 0;
40287         this.loadNext = false;
40288         
40289         if(this.isExpanded()){
40290             this.collapse();
40291             return;
40292         }
40293         
40294         this.hasFocus = true;
40295         
40296         if(this.triggerAction == 'all') {
40297             this.doQuery(this.allQuery, true);
40298             return;
40299         }
40300         
40301         this.doQuery(this.getRawValue());
40302     },
40303     
40304     getCurrency : function()
40305     {   
40306         var v = this.currencyEl().getValue();
40307         
40308         return v;
40309     },
40310     
40311     restrictHeight : function()
40312     {
40313         this.list.alignTo(this.currencyEl(), this.listAlign);
40314         this.list.alignTo(this.currencyEl(), this.listAlign);
40315     },
40316     
40317     onViewClick : function(view, doFocus, el, e)
40318     {
40319         var index = this.view.getSelectedIndexes()[0];
40320         
40321         var r = this.store.getAt(index);
40322         
40323         if(r){
40324             this.onSelect(r, index);
40325         }
40326     },
40327     
40328     onSelect : function(record, index){
40329         
40330         if(this.fireEvent('beforeselect', this, record, index) !== false){
40331         
40332             this.setFromCurrencyData(index > -1 ? record.data : false);
40333             
40334             this.collapse();
40335             
40336             this.fireEvent('select', this, record, index);
40337         }
40338     },
40339     
40340     setFromCurrencyData : function(o)
40341     {
40342         var currency = '';
40343         
40344         this.lastCurrency = o;
40345         
40346         if (this.currencyField) {
40347             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40348         } else {
40349             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40350         }
40351         
40352         this.lastSelectionText = currency;
40353         
40354         this.setCurrency(currency);
40355     },
40356     
40357     setFromData : function(o)
40358     {
40359         var c = {};
40360         
40361         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40362         
40363         this.setFromCurrencyData(c);
40364         
40365         var value = '';
40366         
40367         if (this.name) {
40368             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40369         } else {
40370             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40371         }
40372         
40373         this.setValue(value);
40374         
40375     },
40376     
40377     setCurrency : function(v)
40378     {   
40379         this.currencyValue = v;
40380         
40381         if(this.rendered){
40382             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40383             this.validate();
40384         }
40385     },
40386     
40387     setValue : function(v)
40388     {
40389         v = this.fixPrecision(v);
40390         
40391         v = String(v).replace(".", this.decimalSeparator);
40392         
40393         this.value = v;
40394         
40395         if(this.rendered){
40396             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40397             this.validate();
40398         }
40399     },
40400     
40401     getRawValue : function()
40402     {
40403         var v = this.inputEl().getValue();
40404         
40405         return v;
40406     },
40407     
40408     getValue : function()
40409     {
40410         return this.fixPrecision(this.parseValue(this.getRawValue()));
40411     },
40412     
40413     parseValue : function(value)
40414     {
40415         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40416         return isNaN(value) ? '' : value;
40417     },
40418     
40419     fixPrecision : function(value)
40420     {
40421         var nan = isNaN(value);
40422         
40423         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40424             return nan ? '' : value;
40425         }
40426         
40427         return parseFloat(value).toFixed(this.decimalPrecision);
40428     },
40429     
40430     decimalPrecisionFcn : function(v)
40431     {
40432         return Math.floor(v);
40433     },
40434     
40435     validateValue : function(value)
40436     {
40437         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40438             return false;
40439         }
40440         
40441         var num = this.parseValue(value);
40442         
40443         if(isNaN(num)){
40444             this.markInvalid(String.format(this.nanText, value));
40445             return false;
40446         }
40447         
40448         if(num < this.minValue){
40449             this.markInvalid(String.format(this.minText, this.minValue));
40450             return false;
40451         }
40452         
40453         if(num > this.maxValue){
40454             this.markInvalid(String.format(this.maxText, this.maxValue));
40455             return false;
40456         }
40457         
40458         return true;
40459     },
40460     
40461     validate : function()
40462     {
40463         if(this.disabled || this.allowBlank){
40464             this.markValid();
40465             return true;
40466         }
40467         
40468         var currency = this.getCurrency();
40469         
40470         if(this.validateValue(this.getRawValue()) && currency.length){
40471             this.markValid();
40472             return true;
40473         }
40474         
40475         this.markInvalid();
40476         return false;
40477     },
40478     
40479     getName: function()
40480     {
40481         return this.name;
40482     },
40483     
40484     beforeBlur : function()
40485     {
40486         if(!this.castInt){
40487             return;
40488         }
40489         
40490         var v = this.parseValue(this.getRawValue());
40491         
40492         if(v){
40493             this.setValue(v);
40494         }
40495     },
40496     
40497     onBlur : function()
40498     {
40499         this.beforeBlur();
40500         
40501         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40502             //this.el.removeClass(this.focusClass);
40503         }
40504         
40505         this.hasFocus = false;
40506         
40507         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40508             this.validate();
40509         }
40510         
40511         var v = this.getValue();
40512         
40513         if(String(v) !== String(this.startValue)){
40514             this.fireEvent('change', this, v, this.startValue);
40515         }
40516         
40517         this.fireEvent("blur", this);
40518     },
40519     
40520     inputEl : function()
40521     {
40522         return this.el.select('.roo-money-amount-input', true).first();
40523     },
40524     
40525     currencyEl : function()
40526     {
40527         return this.el.select('.roo-money-currency-input', true).first();
40528     }
40529     
40530 });