roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 Roo.log('start render...');
312                 Roo.log(cn);
313                 Roo.log(this[cntr](true));
314                 if(cn.render && cn.render(this[cntr](true)) === false){
315                     return;
316                 }
317                 
318              }
319             // then add the element..
320         }
321         
322         
323         // handle the kids..
324         
325         var nitems = [];
326         /*
327         if (typeof (tree.menu) != 'undefined') {
328             tree.menu.parentType = cn.xtype;
329             tree.menu.triggerEl = cn.el;
330             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
331             
332         }
333         */
334         if (!tree.items || !tree.items.length) {
335             cn.items = nitems;
336             //Roo.log(["no children", this]);
337             
338             return cn;
339         }
340          
341         var items = tree.items;
342         delete tree.items;
343         
344         //Roo.log(items.length);
345             // add the items..
346         if (!skip_children) {    
347             for(var i =0;i < items.length;i++) {
348               //  Roo.log(['add child', items[i]]);
349                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
350             }
351         }
352         
353         cn.items = nitems;
354         
355         //Roo.log("fire childrenrendered");
356         
357         cn.fireEvent('childrenrendered', this);
358         
359         return cn;
360     },
361     /**
362      * Show a component - removes 'hidden' class
363      */
364     show : function()
365     {
366         if (this.el) {
367             this.el.removeClass('hidden');
368         }
369     },
370     /**
371      * Hide a component - adds 'hidden' class
372      */
373     hide: function()
374     {
375         if (this.el && !this.el.hasClass('hidden')) {
376             this.el.addClass('hidden');
377         }
378     }
379 });
380
381  /*
382  * - LGPL
383  *
384  * Body
385  *
386  */
387
388 /**
389  * @class Roo.bootstrap.Body
390  * @extends Roo.bootstrap.Component
391  * Bootstrap Body class
392  *
393  * @constructor
394  * Create a new body
395  * @param {Object} config The config object
396  */
397
398 Roo.bootstrap.Body = function(config){
399
400     config = config || {};
401
402     Roo.bootstrap.Body.superclass.constructor.call(this, config);
403     this.el = Roo.get(config.el ? config.el : document.body );
404     if (this.cls && this.cls.length) {
405         Roo.get(document.body).addClass(this.cls);
406     }
407 };
408
409 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
410
411     is_body : true,// just to make sure it's constructed?
412
413         autoCreate : {
414         cls: 'container'
415     },
416     onRender : function(ct, position)
417     {
418        /* Roo.log("Roo.bootstrap.Body - onRender");
419         if (this.cls && this.cls.length) {
420             Roo.get(document.body).addClass(this.cls);
421         }
422         // style??? xttr???
423         */
424     }
425
426
427
428
429 });
430 /*
431  * - LGPL
432  *
433  * button group
434  * 
435  */
436
437
438 /**
439  * @class Roo.bootstrap.ButtonGroup
440  * @extends Roo.bootstrap.Component
441  * Bootstrap ButtonGroup class
442  * @cfg {String} size lg | sm | xs (default empty normal)
443  * @cfg {String} align vertical | justified  (default none)
444  * @cfg {String} direction up | down (default down)
445  * @cfg {Boolean} toolbar false | true
446  * @cfg {Boolean} btn true | false
447  * 
448  * 
449  * @constructor
450  * Create a new Input
451  * @param {Object} config The config object
452  */
453
454 Roo.bootstrap.ButtonGroup = function(config){
455     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
456 };
457
458 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
459     
460     size: '',
461     align: '',
462     direction: '',
463     toolbar: false,
464     btn: true,
465
466     getAutoCreate : function(){
467         var cfg = {
468             cls: 'btn-group',
469             html : null
470         };
471         
472         cfg.html = this.html || cfg.html;
473         
474         if (this.toolbar) {
475             cfg = {
476                 cls: 'btn-toolbar',
477                 html: null
478             };
479             
480             return cfg;
481         }
482         
483         if (['vertical','justified'].indexOf(this.align)!==-1) {
484             cfg.cls = 'btn-group-' + this.align;
485             
486             if (this.align == 'justified') {
487                 console.log(this.items);
488             }
489         }
490         
491         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
492             cfg.cls += ' btn-group-' + this.size;
493         }
494         
495         if (this.direction == 'up') {
496             cfg.cls += ' dropup' ;
497         }
498         
499         return cfg;
500     }
501    
502 });
503
504  /*
505  * - LGPL
506  *
507  * button
508  * 
509  */
510
511 /**
512  * @class Roo.bootstrap.Button
513  * @extends Roo.bootstrap.Component
514  * Bootstrap Button class
515  * @cfg {String} html The button content
516  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
517  * @cfg {String} size ( lg | sm | xs)
518  * @cfg {String} tag ( a | input | submit)
519  * @cfg {String} href empty or href
520  * @cfg {Boolean} disabled default false;
521  * @cfg {Boolean} isClose default false;
522  * @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)
523  * @cfg {String} badge text for badge
524  * @cfg {String} theme default 
525  * @cfg {Boolean} inverse 
526  * @cfg {Boolean} toggle 
527  * @cfg {String} ontext text for on toggle state
528  * @cfg {String} offtext text for off toggle state
529  * @cfg {Boolean} defaulton 
530  * @cfg {Boolean} preventDefault  default true
531  * @cfg {Boolean} removeClass remove the standard class..
532  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
533  * 
534  * @constructor
535  * Create a new button
536  * @param {Object} config The config object
537  */
538
539
540 Roo.bootstrap.Button = function(config){
541     Roo.bootstrap.Button.superclass.constructor.call(this, config);
542     this.weightClass = ["btn-default", 
543                        "btn-primary", 
544                        "btn-success", 
545                        "btn-info", 
546                        "btn-warning",
547                        "btn-danger",
548                        "btn-link"
549                       ],  
550     this.addEvents({
551         // raw events
552         /**
553          * @event click
554          * When a butotn is pressed
555          * @param {Roo.bootstrap.Button} this
556          * @param {Roo.EventObject} e
557          */
558         "click" : true,
559          /**
560          * @event toggle
561          * After the button has been toggles
562          * @param {Roo.EventObject} e
563          * @param {boolean} pressed (also available as button.pressed)
564          */
565         "toggle" : true
566     });
567 };
568
569 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
570     html: false,
571     active: false,
572     weight: '',
573     size: '',
574     tag: 'button',
575     href: '',
576     disabled: false,
577     isClose: false,
578     glyphicon: '',
579     badge: '',
580     theme: 'default',
581     inverse: false,
582     
583     toggle: false,
584     ontext: 'ON',
585     offtext: 'OFF',
586     defaulton: true,
587     preventDefault: true,
588     removeClass: false,
589     name: false,
590     target: false,
591     
592     
593     pressed : null,
594      
595     
596     getAutoCreate : function(){
597         
598         var cfg = {
599             tag : 'button',
600             cls : 'roo-button',
601             html: ''
602         };
603         
604         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
605             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
606             this.tag = 'button';
607         } else {
608             cfg.tag = this.tag;
609         }
610         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
611         
612         if (this.toggle == true) {
613             cfg={
614                 tag: 'div',
615                 cls: 'slider-frame roo-button',
616                 cn: [
617                     {
618                         tag: 'span',
619                         'data-on-text':'ON',
620                         'data-off-text':'OFF',
621                         cls: 'slider-button',
622                         html: this.offtext
623                     }
624                 ]
625             };
626             
627             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
628                 cfg.cls += ' '+this.weight;
629             }
630             
631             return cfg;
632         }
633         
634         if (this.isClose) {
635             cfg.cls += ' close';
636             
637             cfg["aria-hidden"] = true;
638             
639             cfg.html = "&times;";
640             
641             return cfg;
642         }
643         
644          
645         if (this.theme==='default') {
646             cfg.cls = 'btn roo-button';
647             
648             //if (this.parentType != 'Navbar') {
649             this.weight = this.weight.length ?  this.weight : 'default';
650             //}
651             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
652                 
653                 cfg.cls += ' btn-' + this.weight;
654             }
655         } else if (this.theme==='glow') {
656             
657             cfg.tag = 'a';
658             cfg.cls = 'btn-glow roo-button';
659             
660             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
661                 
662                 cfg.cls += ' ' + this.weight;
663             }
664         }
665    
666         
667         if (this.inverse) {
668             this.cls += ' inverse';
669         }
670         
671         
672         if (this.active) {
673             cfg.cls += ' active';
674         }
675         
676         if (this.disabled) {
677             cfg.disabled = 'disabled';
678         }
679         
680         if (this.items) {
681             Roo.log('changing to ul' );
682             cfg.tag = 'ul';
683             this.glyphicon = 'caret';
684         }
685         
686         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
687          
688         //gsRoo.log(this.parentType);
689         if (this.parentType === 'Navbar' && !this.parent().bar) {
690             Roo.log('changing to li?');
691             
692             cfg.tag = 'li';
693             
694             cfg.cls = '';
695             cfg.cn =  [{
696                 tag : 'a',
697                 cls : 'roo-button',
698                 html : this.html,
699                 href : this.href || '#'
700             }];
701             if (this.menu) {
702                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
703                 cfg.cls += ' dropdown';
704             }   
705             
706             delete cfg.html;
707             
708         }
709         
710        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
711         
712         if (this.glyphicon) {
713             cfg.html = ' ' + cfg.html;
714             
715             cfg.cn = [
716                 {
717                     tag: 'span',
718                     cls: 'glyphicon glyphicon-' + this.glyphicon
719                 }
720             ];
721         }
722         
723         if (this.badge) {
724             cfg.html += ' ';
725             
726             cfg.tag = 'a';
727             
728 //            cfg.cls='btn roo-button';
729             
730             cfg.href=this.href;
731             
732             var value = cfg.html;
733             
734             if(this.glyphicon){
735                 value = {
736                             tag: 'span',
737                             cls: 'glyphicon glyphicon-' + this.glyphicon,
738                             html: this.html
739                         };
740                 
741             }
742             
743             cfg.cn = [
744                 value,
745                 {
746                     tag: 'span',
747                     cls: 'badge',
748                     html: this.badge
749                 }
750             ];
751             
752             cfg.html='';
753         }
754         
755         if (this.menu) {
756             cfg.cls += ' dropdown';
757             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
758         }
759         
760         if (cfg.tag !== 'a' && this.href !== '') {
761             throw "Tag must be a to set href.";
762         } else if (this.href.length > 0) {
763             cfg.href = this.href;
764         }
765         
766         if(this.removeClass){
767             cfg.cls = '';
768         }
769         
770         if(this.target){
771             cfg.target = this.target;
772         }
773         
774         return cfg;
775     },
776     initEvents: function() {
777        // Roo.log('init events?');
778 //        Roo.log(this.el.dom);
779         // add the menu...
780         
781         if (typeof (this.menu) != 'undefined') {
782             this.menu.parentType = this.xtype;
783             this.menu.triggerEl = this.el;
784             this.addxtype(Roo.apply({}, this.menu));
785         }
786
787
788        if (this.el.hasClass('roo-button')) {
789             this.el.on('click', this.onClick, this);
790        } else {
791             this.el.select('.roo-button').on('click', this.onClick, this);
792        }
793        
794        if(this.removeClass){
795            this.el.on('click', this.onClick, this);
796        }
797        
798        this.el.enableDisplayMode();
799         
800     },
801     onClick : function(e)
802     {
803         if (this.disabled) {
804             return;
805         }
806         
807         
808         Roo.log('button on click ');
809         if(this.preventDefault){
810             e.preventDefault();
811         }
812         if (this.pressed === true || this.pressed === false) {
813             this.pressed = !this.pressed;
814             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
815             this.fireEvent('toggle', this, e, this.pressed);
816         }
817         
818         
819         this.fireEvent('click', this, e);
820     },
821     
822     /**
823      * Enables this button
824      */
825     enable : function()
826     {
827         this.disabled = false;
828         this.el.removeClass('disabled');
829     },
830     
831     /**
832      * Disable this button
833      */
834     disable : function()
835     {
836         this.disabled = true;
837         this.el.addClass('disabled');
838     },
839      /**
840      * sets the active state on/off, 
841      * @param {Boolean} state (optional) Force a particular state
842      */
843     setActive : function(v) {
844         
845         this.el[v ? 'addClass' : 'removeClass']('active');
846     },
847      /**
848      * toggles the current active state 
849      */
850     toggleActive : function()
851     {
852        var active = this.el.hasClass('active');
853        this.setActive(!active);
854        
855         
856     },
857     setText : function(str)
858     {
859         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
860     },
861     getText : function()
862     {
863         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
864     },
865     hide: function() {
866        
867      
868         this.el.hide();   
869     },
870     show: function() {
871        
872         this.el.show();   
873     },
874     setWeight : function(str)
875     {
876           this.el.removeClass(this.weightClass);
877         this.el.addClass('btn-' + str);        
878     }
879     
880     
881 });
882
883  /*
884  * - LGPL
885  *
886  * column
887  * 
888  */
889
890 /**
891  * @class Roo.bootstrap.Column
892  * @extends Roo.bootstrap.Component
893  * Bootstrap Column class
894  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
895  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
896  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
897  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
898  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
899  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
900  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
901  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
902  *
903  * 
904  * @cfg {Boolean} hidden (true|false) hide the element
905  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
906  * @cfg {String} fa (ban|check|...) font awesome icon
907  * @cfg {Number} fasize (1|2|....) font awsome size
908
909  * @cfg {String} icon (info-sign|check|...) glyphicon name
910
911  * @cfg {String} html content of column.
912  * 
913  * @constructor
914  * Create a new Column
915  * @param {Object} config The config object
916  */
917
918 Roo.bootstrap.Column = function(config){
919     Roo.bootstrap.Column.superclass.constructor.call(this, config);
920 };
921
922 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
923     
924     xs: false,
925     sm: false,
926     md: false,
927     lg: false,
928     xsoff: false,
929     smoff: false,
930     mdoff: false,
931     lgoff: false,
932     html: '',
933     offset: 0,
934     alert: false,
935     fa: false,
936     icon : false,
937     hidden : false,
938     fasize : 1,
939     
940     getAutoCreate : function(){
941         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
942         
943         cfg = {
944             tag: 'div',
945             cls: 'column'
946         };
947         
948         var settings=this;
949         ['xs','sm','md','lg'].map(function(size){
950             //Roo.log( size + ':' + settings[size]);
951             
952             if (settings[size+'off'] !== false) {
953                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
954             }
955             
956             if (settings[size] === false) {
957                 return;
958             }
959             
960             if (!settings[size]) { // 0 = hidden
961                 cfg.cls += ' hidden-' + size;
962                 return;
963             }
964             cfg.cls += ' col-' + size + '-' + settings[size];
965             
966         });
967         
968         if (this.hidden) {
969             cfg.cls += ' hidden';
970         }
971         
972         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
973             cfg.cls +=' alert alert-' + this.alert;
974         }
975         
976         
977         if (this.html.length) {
978             cfg.html = this.html;
979         }
980         if (this.fa) {
981             var fasize = '';
982             if (this.fasize > 1) {
983                 fasize = ' fa-' + this.fasize + 'x';
984             }
985             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
986             
987             
988         }
989         if (this.icon) {
990             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
991         }
992         
993         return cfg;
994     }
995    
996 });
997
998  
999
1000  /*
1001  * - LGPL
1002  *
1003  * page container.
1004  * 
1005  */
1006
1007
1008 /**
1009  * @class Roo.bootstrap.Container
1010  * @extends Roo.bootstrap.Component
1011  * Bootstrap Container class
1012  * @cfg {Boolean} jumbotron is it a jumbotron element
1013  * @cfg {String} html content of element
1014  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1015  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1016  * @cfg {String} header content of header (for panel)
1017  * @cfg {String} footer content of footer (for panel)
1018  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1019  * @cfg {String} tag (header|aside|section) type of HTML tag.
1020  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1021  * @cfg {String} fa font awesome icon
1022  * @cfg {String} icon (info-sign|check|...) glyphicon name
1023  * @cfg {Boolean} hidden (true|false) hide the element
1024  * @cfg {Boolean} expandable (true|false) default false
1025  * @cfg {Boolean} expanded (true|false) default true
1026  * @cfg {String} rheader contet on the right of header
1027  * @cfg {Boolean} clickable (true|false) default false
1028
1029  *     
1030  * @constructor
1031  * Create a new Container
1032  * @param {Object} config The config object
1033  */
1034
1035 Roo.bootstrap.Container = function(config){
1036     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1037     
1038     this.addEvents({
1039         // raw events
1040          /**
1041          * @event expand
1042          * After the panel has been expand
1043          * 
1044          * @param {Roo.bootstrap.Container} this
1045          */
1046         "expand" : true,
1047         /**
1048          * @event collapse
1049          * After the panel has been collapsed
1050          * 
1051          * @param {Roo.bootstrap.Container} this
1052          */
1053         "collapse" : true,
1054         /**
1055          * @event click
1056          * When a element is chick
1057          * @param {Roo.bootstrap.Container} this
1058          * @param {Roo.EventObject} e
1059          */
1060         "click" : true
1061     });
1062 };
1063
1064 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1065     
1066     jumbotron : false,
1067     well: '',
1068     panel : '',
1069     header: '',
1070     footer : '',
1071     sticky: '',
1072     tag : false,
1073     alert : false,
1074     fa: false,
1075     icon : false,
1076     expandable : false,
1077     rheader : '',
1078     expanded : true,
1079     clickable: false,
1080   
1081      
1082     getChildContainer : function() {
1083         
1084         if(!this.el){
1085             return false;
1086         }
1087         
1088         if (this.panel.length) {
1089             return this.el.select('.panel-body',true).first();
1090         }
1091         
1092         return this.el;
1093     },
1094     
1095     
1096     getAutoCreate : function(){
1097         
1098         var cfg = {
1099             tag : this.tag || 'div',
1100             html : '',
1101             cls : ''
1102         };
1103         if (this.jumbotron) {
1104             cfg.cls = 'jumbotron';
1105         }
1106         
1107         
1108         
1109         // - this is applied by the parent..
1110         //if (this.cls) {
1111         //    cfg.cls = this.cls + '';
1112         //}
1113         
1114         if (this.sticky.length) {
1115             
1116             var bd = Roo.get(document.body);
1117             if (!bd.hasClass('bootstrap-sticky')) {
1118                 bd.addClass('bootstrap-sticky');
1119                 Roo.select('html',true).setStyle('height', '100%');
1120             }
1121              
1122             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1123         }
1124         
1125         
1126         if (this.well.length) {
1127             switch (this.well) {
1128                 case 'lg':
1129                 case 'sm':
1130                     cfg.cls +=' well well-' +this.well;
1131                     break;
1132                 default:
1133                     cfg.cls +=' well';
1134                     break;
1135             }
1136         }
1137         
1138         if (this.hidden) {
1139             cfg.cls += ' hidden';
1140         }
1141         
1142         
1143         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1144             cfg.cls +=' alert alert-' + this.alert;
1145         }
1146         
1147         var body = cfg;
1148         
1149         if (this.panel.length) {
1150             cfg.cls += ' panel panel-' + this.panel;
1151             cfg.cn = [];
1152             if (this.header.length) {
1153                 
1154                 var h = [];
1155                 
1156                 if(this.expandable){
1157                     
1158                     cfg.cls = cfg.cls + ' expandable';
1159                     
1160                     h.push({
1161                         tag: 'i',
1162                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1163                     });
1164                     
1165                 }
1166                 
1167                 h.push(
1168                     {
1169                         tag: 'span',
1170                         cls : 'panel-title',
1171                         html : (this.expandable ? '&nbsp;' : '') + this.header
1172                     },
1173                     {
1174                         tag: 'span',
1175                         cls: 'panel-header-right',
1176                         html: this.rheader
1177                     }
1178                 );
1179                 
1180                 cfg.cn.push({
1181                     cls : 'panel-heading',
1182                     style : this.expandable ? 'cursor: pointer' : '',
1183                     cn : h
1184                 });
1185                 
1186             }
1187             
1188             body = false;
1189             cfg.cn.push({
1190                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1191                 html : this.html
1192             });
1193             
1194             
1195             if (this.footer.length) {
1196                 cfg.cn.push({
1197                     cls : 'panel-footer',
1198                     html : this.footer
1199                     
1200                 });
1201             }
1202             
1203         }
1204         
1205         if (body) {
1206             body.html = this.html || cfg.html;
1207             // prefix with the icons..
1208             if (this.fa) {
1209                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1210             }
1211             if (this.icon) {
1212                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1213             }
1214             
1215             
1216         }
1217         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1218             cfg.cls =  'container';
1219         }
1220         
1221         return cfg;
1222     },
1223     
1224     initEvents: function() 
1225     {
1226         if(this.expandable){
1227             var headerEl = this.headerEl();
1228         
1229             if(headerEl){
1230                 headerEl.on('click', this.onToggleClick, this);
1231             }
1232         }
1233         
1234         if(this.clickable){
1235             this.el.on('click', this.onClick, this);
1236         }
1237         
1238     },
1239     
1240     onToggleClick : function()
1241     {
1242         var headerEl = this.headerEl();
1243         
1244         if(!headerEl){
1245             return;
1246         }
1247         
1248         if(this.expanded){
1249             this.collapse();
1250             return;
1251         }
1252         
1253         this.expand();
1254     },
1255     
1256     expand : function()
1257     {
1258         if(this.fireEvent('expand', this)) {
1259             
1260             this.expanded = true;
1261             
1262             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1263             
1264             this.el.select('.panel-body',true).first().removeClass('hide');
1265             
1266             var toggleEl = this.toggleEl();
1267
1268             if(!toggleEl){
1269                 return;
1270             }
1271
1272             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1273         }
1274         
1275     },
1276     
1277     collapse : function()
1278     {
1279         if(this.fireEvent('collapse', this)) {
1280             
1281             this.expanded = false;
1282             
1283             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1284             this.el.select('.panel-body',true).first().addClass('hide');
1285         
1286             var toggleEl = this.toggleEl();
1287
1288             if(!toggleEl){
1289                 return;
1290             }
1291
1292             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1293         }
1294     },
1295     
1296     toggleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-heading .fa',true).first();
1303     },
1304     
1305     headerEl : function()
1306     {
1307         if(!this.el || !this.panel.length || !this.header.length){
1308             return;
1309         }
1310         
1311         return this.el.select('.panel-heading',true).first()
1312     },
1313     
1314     bodyEl : function()
1315     {
1316         if(!this.el || !this.panel.length){
1317             return;
1318         }
1319         
1320         return this.el.select('.panel-body',true).first()
1321     },
1322     
1323     titleEl : function()
1324     {
1325         if(!this.el || !this.panel.length || !this.header.length){
1326             return;
1327         }
1328         
1329         return this.el.select('.panel-title',true).first();
1330     },
1331     
1332     setTitle : function(v)
1333     {
1334         var titleEl = this.titleEl();
1335         
1336         if(!titleEl){
1337             return;
1338         }
1339         
1340         titleEl.dom.innerHTML = v;
1341     },
1342     
1343     getTitle : function()
1344     {
1345         
1346         var titleEl = this.titleEl();
1347         
1348         if(!titleEl){
1349             return '';
1350         }
1351         
1352         return titleEl.dom.innerHTML;
1353     },
1354     
1355     setRightTitle : function(v)
1356     {
1357         var t = this.el.select('.panel-header-right',true).first();
1358         
1359         if(!t){
1360             return;
1361         }
1362         
1363         t.dom.innerHTML = v;
1364     },
1365     
1366     onClick : function(e)
1367     {
1368         e.preventDefault();
1369         
1370         this.fireEvent('click', this, e);
1371     }
1372    
1373 });
1374
1375  /*
1376  * - LGPL
1377  *
1378  * image
1379  * 
1380  */
1381
1382
1383 /**
1384  * @class Roo.bootstrap.Img
1385  * @extends Roo.bootstrap.Component
1386  * Bootstrap Img class
1387  * @cfg {Boolean} imgResponsive false | true
1388  * @cfg {String} border rounded | circle | thumbnail
1389  * @cfg {String} src image source
1390  * @cfg {String} alt image alternative text
1391  * @cfg {String} href a tag href
1392  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1393  * @cfg {String} xsUrl xs image source
1394  * @cfg {String} smUrl sm image source
1395  * @cfg {String} mdUrl md image source
1396  * @cfg {String} lgUrl lg image source
1397  * 
1398  * @constructor
1399  * Create a new Input
1400  * @param {Object} config The config object
1401  */
1402
1403 Roo.bootstrap.Img = function(config){
1404     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1405     
1406     this.addEvents({
1407         // img events
1408         /**
1409          * @event click
1410          * The img click event for the img.
1411          * @param {Roo.EventObject} e
1412          */
1413         "click" : true
1414     });
1415 };
1416
1417 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1418     
1419     imgResponsive: true,
1420     border: '',
1421     src: 'about:blank',
1422     href: false,
1423     target: false,
1424     xsUrl: '',
1425     smUrl: '',
1426     mdUrl: '',
1427     lgUrl: '',
1428
1429     getAutoCreate : function()
1430     {   
1431         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1432             return this.createSingleImg();
1433         }
1434         
1435         var cfg = {
1436             tag: 'div',
1437             cls: 'roo-image-responsive-group',
1438             cn: []
1439         };
1440         var _this = this;
1441         
1442         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1443             
1444             if(!_this[size + 'Url']){
1445                 return;
1446             }
1447             
1448             var img = {
1449                 tag: 'img',
1450                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1451                 html: _this.html || cfg.html,
1452                 src: _this[size + 'Url']
1453             };
1454             
1455             img.cls += ' roo-image-responsive-' + size;
1456             
1457             var s = ['xs', 'sm', 'md', 'lg'];
1458             
1459             s.splice(s.indexOf(size), 1);
1460             
1461             Roo.each(s, function(ss){
1462                 img.cls += ' hidden-' + ss;
1463             });
1464             
1465             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1466                 cfg.cls += ' img-' + _this.border;
1467             }
1468             
1469             if(_this.alt){
1470                 cfg.alt = _this.alt;
1471             }
1472             
1473             if(_this.href){
1474                 var a = {
1475                     tag: 'a',
1476                     href: _this.href,
1477                     cn: [
1478                         img
1479                     ]
1480                 };
1481
1482                 if(this.target){
1483                     a.target = _this.target;
1484                 }
1485             }
1486             
1487             cfg.cn.push((_this.href) ? a : img);
1488             
1489         });
1490         
1491         return cfg;
1492     },
1493     
1494     createSingleImg : function()
1495     {
1496         var cfg = {
1497             tag: 'img',
1498             cls: (this.imgResponsive) ? 'img-responsive' : '',
1499             html : null,
1500             src : 'about:blank'  // just incase src get's set to undefined?!?
1501         };
1502         
1503         cfg.html = this.html || cfg.html;
1504         
1505         cfg.src = this.src || cfg.src;
1506         
1507         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1508             cfg.cls += ' img-' + this.border;
1509         }
1510         
1511         if(this.alt){
1512             cfg.alt = this.alt;
1513         }
1514         
1515         if(this.href){
1516             var a = {
1517                 tag: 'a',
1518                 href: this.href,
1519                 cn: [
1520                     cfg
1521                 ]
1522             };
1523             
1524             if(this.target){
1525                 a.target = this.target;
1526             }
1527             
1528         }
1529         
1530         return (this.href) ? a : cfg;
1531     },
1532     
1533     initEvents: function() 
1534     {
1535         if(!this.href){
1536             this.el.on('click', this.onClick, this);
1537         }
1538         
1539     },
1540     
1541     onClick : function(e)
1542     {
1543         Roo.log('img onclick');
1544         this.fireEvent('click', this, e);
1545     },
1546     /**
1547      * Sets the url of the image - used to update it
1548      * @param {String} url the url of the image
1549      */
1550     
1551     setSrc : function(url)
1552     {
1553         this.src =  url;
1554         
1555         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1556             this.el.dom.src =  url;
1557             return;
1558         }
1559         
1560         this.el.select('img', true).first().dom.src =  url;
1561     }
1562     
1563     
1564    
1565 });
1566
1567  /*
1568  * - LGPL
1569  *
1570  * image
1571  * 
1572  */
1573
1574
1575 /**
1576  * @class Roo.bootstrap.Link
1577  * @extends Roo.bootstrap.Component
1578  * Bootstrap Link Class
1579  * @cfg {String} alt image alternative text
1580  * @cfg {String} href a tag href
1581  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1582  * @cfg {String} html the content of the link.
1583  * @cfg {String} anchor name for the anchor link
1584  * @cfg {String} fa - favicon
1585
1586  * @cfg {Boolean} preventDefault (true | false) default false
1587
1588  * 
1589  * @constructor
1590  * Create a new Input
1591  * @param {Object} config The config object
1592  */
1593
1594 Roo.bootstrap.Link = function(config){
1595     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1596     
1597     this.addEvents({
1598         // img events
1599         /**
1600          * @event click
1601          * The img click event for the img.
1602          * @param {Roo.EventObject} e
1603          */
1604         "click" : true
1605     });
1606 };
1607
1608 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1609     
1610     href: false,
1611     target: false,
1612     preventDefault: false,
1613     anchor : false,
1614     alt : false,
1615     fa: false,
1616
1617
1618     getAutoCreate : function()
1619     {
1620         var html = this.html || '';
1621         
1622         if (this.fa !== false) {
1623             html = '<i class="fa fa-' + this.fa + '"></i>';
1624         }
1625         var cfg = {
1626             tag: 'a'
1627         };
1628         // anchor's do not require html/href...
1629         if (this.anchor === false) {
1630             cfg.html = html;
1631             cfg.href = this.href || '#';
1632         } else {
1633             cfg.name = this.anchor;
1634             if (this.html !== false || this.fa !== false) {
1635                 cfg.html = html;
1636             }
1637             if (this.href !== false) {
1638                 cfg.href = this.href;
1639             }
1640         }
1641         
1642         if(this.alt !== false){
1643             cfg.alt = this.alt;
1644         }
1645         
1646         
1647         if(this.target !== false) {
1648             cfg.target = this.target;
1649         }
1650         
1651         return cfg;
1652     },
1653     
1654     initEvents: function() {
1655         
1656         if(!this.href || this.preventDefault){
1657             this.el.on('click', this.onClick, this);
1658         }
1659     },
1660     
1661     onClick : function(e)
1662     {
1663         if(this.preventDefault){
1664             e.preventDefault();
1665         }
1666         //Roo.log('img onclick');
1667         this.fireEvent('click', this, e);
1668     }
1669    
1670 });
1671
1672  /*
1673  * - LGPL
1674  *
1675  * header
1676  * 
1677  */
1678
1679 /**
1680  * @class Roo.bootstrap.Header
1681  * @extends Roo.bootstrap.Component
1682  * Bootstrap Header class
1683  * @cfg {String} html content of header
1684  * @cfg {Number} level (1|2|3|4|5|6) default 1
1685  * 
1686  * @constructor
1687  * Create a new Header
1688  * @param {Object} config The config object
1689  */
1690
1691
1692 Roo.bootstrap.Header  = function(config){
1693     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1694 };
1695
1696 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1697     
1698     //href : false,
1699     html : false,
1700     level : 1,
1701     
1702     
1703     
1704     getAutoCreate : function(){
1705         
1706         
1707         
1708         var cfg = {
1709             tag: 'h' + (1 *this.level),
1710             html: this.html || ''
1711         } ;
1712         
1713         return cfg;
1714     }
1715    
1716 });
1717
1718  
1719
1720  /*
1721  * Based on:
1722  * Ext JS Library 1.1.1
1723  * Copyright(c) 2006-2007, Ext JS, LLC.
1724  *
1725  * Originally Released Under LGPL - original licence link has changed is not relivant.
1726  *
1727  * Fork - LGPL
1728  * <script type="text/javascript">
1729  */
1730  
1731 /**
1732  * @class Roo.bootstrap.MenuMgr
1733  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1734  * @singleton
1735  */
1736 Roo.bootstrap.MenuMgr = function(){
1737    var menus, active, groups = {}, attached = false, lastShow = new Date();
1738
1739    // private - called when first menu is created
1740    function init(){
1741        menus = {};
1742        active = new Roo.util.MixedCollection();
1743        Roo.get(document).addKeyListener(27, function(){
1744            if(active.length > 0){
1745                hideAll();
1746            }
1747        });
1748    }
1749
1750    // private
1751    function hideAll(){
1752        if(active && active.length > 0){
1753            var c = active.clone();
1754            c.each(function(m){
1755                m.hide();
1756            });
1757        }
1758    }
1759
1760    // private
1761    function onHide(m){
1762        active.remove(m);
1763        if(active.length < 1){
1764            Roo.get(document).un("mouseup", onMouseDown);
1765             
1766            attached = false;
1767        }
1768    }
1769
1770    // private
1771    function onShow(m){
1772        var last = active.last();
1773        lastShow = new Date();
1774        active.add(m);
1775        if(!attached){
1776           Roo.get(document).on("mouseup", onMouseDown);
1777            
1778            attached = true;
1779        }
1780        if(m.parentMenu){
1781           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1782           m.parentMenu.activeChild = m;
1783        }else if(last && last.isVisible()){
1784           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1785        }
1786    }
1787
1788    // private
1789    function onBeforeHide(m){
1790        if(m.activeChild){
1791            m.activeChild.hide();
1792        }
1793        if(m.autoHideTimer){
1794            clearTimeout(m.autoHideTimer);
1795            delete m.autoHideTimer;
1796        }
1797    }
1798
1799    // private
1800    function onBeforeShow(m){
1801        var pm = m.parentMenu;
1802        if(!pm && !m.allowOtherMenus){
1803            hideAll();
1804        }else if(pm && pm.activeChild && active != m){
1805            pm.activeChild.hide();
1806        }
1807    }
1808
1809    // private this should really trigger on mouseup..
1810    function onMouseDown(e){
1811         Roo.log("on Mouse Up");
1812         
1813         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1814             Roo.log("MenuManager hideAll");
1815             hideAll();
1816             e.stopEvent();
1817         }
1818         
1819         
1820    }
1821
1822    // private
1823    function onBeforeCheck(mi, state){
1824        if(state){
1825            var g = groups[mi.group];
1826            for(var i = 0, l = g.length; i < l; i++){
1827                if(g[i] != mi){
1828                    g[i].setChecked(false);
1829                }
1830            }
1831        }
1832    }
1833
1834    return {
1835
1836        /**
1837         * Hides all menus that are currently visible
1838         */
1839        hideAll : function(){
1840             hideAll();  
1841        },
1842
1843        // private
1844        register : function(menu){
1845            if(!menus){
1846                init();
1847            }
1848            menus[menu.id] = menu;
1849            menu.on("beforehide", onBeforeHide);
1850            menu.on("hide", onHide);
1851            menu.on("beforeshow", onBeforeShow);
1852            menu.on("show", onShow);
1853            var g = menu.group;
1854            if(g && menu.events["checkchange"]){
1855                if(!groups[g]){
1856                    groups[g] = [];
1857                }
1858                groups[g].push(menu);
1859                menu.on("checkchange", onCheck);
1860            }
1861        },
1862
1863         /**
1864          * Returns a {@link Roo.menu.Menu} object
1865          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1866          * be used to generate and return a new Menu instance.
1867          */
1868        get : function(menu){
1869            if(typeof menu == "string"){ // menu id
1870                return menus[menu];
1871            }else if(menu.events){  // menu instance
1872                return menu;
1873            }
1874            /*else if(typeof menu.length == 'number'){ // array of menu items?
1875                return new Roo.bootstrap.Menu({items:menu});
1876            }else{ // otherwise, must be a config
1877                return new Roo.bootstrap.Menu(menu);
1878            }
1879            */
1880            return false;
1881        },
1882
1883        // private
1884        unregister : function(menu){
1885            delete menus[menu.id];
1886            menu.un("beforehide", onBeforeHide);
1887            menu.un("hide", onHide);
1888            menu.un("beforeshow", onBeforeShow);
1889            menu.un("show", onShow);
1890            var g = menu.group;
1891            if(g && menu.events["checkchange"]){
1892                groups[g].remove(menu);
1893                menu.un("checkchange", onCheck);
1894            }
1895        },
1896
1897        // private
1898        registerCheckable : function(menuItem){
1899            var g = menuItem.group;
1900            if(g){
1901                if(!groups[g]){
1902                    groups[g] = [];
1903                }
1904                groups[g].push(menuItem);
1905                menuItem.on("beforecheckchange", onBeforeCheck);
1906            }
1907        },
1908
1909        // private
1910        unregisterCheckable : function(menuItem){
1911            var g = menuItem.group;
1912            if(g){
1913                groups[g].remove(menuItem);
1914                menuItem.un("beforecheckchange", onBeforeCheck);
1915            }
1916        }
1917    };
1918 }();/*
1919  * - LGPL
1920  *
1921  * menu
1922  * 
1923  */
1924
1925 /**
1926  * @class Roo.bootstrap.Menu
1927  * @extends Roo.bootstrap.Component
1928  * Bootstrap Menu class - container for MenuItems
1929  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1930  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1931  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1932  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1933  * 
1934  * @constructor
1935  * Create a new Menu
1936  * @param {Object} config The config object
1937  */
1938
1939
1940 Roo.bootstrap.Menu = function(config){
1941     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1942     if (this.registerMenu && this.type != 'treeview')  {
1943         Roo.bootstrap.MenuMgr.register(this);
1944     }
1945     this.addEvents({
1946         /**
1947          * @event beforeshow
1948          * Fires before this menu is displayed
1949          * @param {Roo.menu.Menu} this
1950          */
1951         beforeshow : true,
1952         /**
1953          * @event beforehide
1954          * Fires before this menu is hidden
1955          * @param {Roo.menu.Menu} this
1956          */
1957         beforehide : true,
1958         /**
1959          * @event show
1960          * Fires after this menu is displayed
1961          * @param {Roo.menu.Menu} this
1962          */
1963         show : true,
1964         /**
1965          * @event hide
1966          * Fires after this menu is hidden
1967          * @param {Roo.menu.Menu} this
1968          */
1969         hide : true,
1970         /**
1971          * @event click
1972          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1973          * @param {Roo.menu.Menu} this
1974          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1975          * @param {Roo.EventObject} e
1976          */
1977         click : true,
1978         /**
1979          * @event mouseover
1980          * Fires when the mouse is hovering over this menu
1981          * @param {Roo.menu.Menu} this
1982          * @param {Roo.EventObject} e
1983          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1984          */
1985         mouseover : true,
1986         /**
1987          * @event mouseout
1988          * Fires when the mouse exits this menu
1989          * @param {Roo.menu.Menu} this
1990          * @param {Roo.EventObject} e
1991          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1992          */
1993         mouseout : true,
1994         /**
1995          * @event itemclick
1996          * Fires when a menu item contained in this menu is clicked
1997          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1998          * @param {Roo.EventObject} e
1999          */
2000         itemclick: true
2001     });
2002     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2003 };
2004
2005 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2006     
2007    /// html : false,
2008     //align : '',
2009     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2010     type: false,
2011     /**
2012      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2013      */
2014     registerMenu : true,
2015     
2016     menuItems :false, // stores the menu items..
2017     
2018     hidden:true,
2019         
2020     parentMenu : false,
2021     
2022     stopEvent : true,
2023     
2024     isLink : false,
2025     
2026     getChildContainer : function() {
2027         return this.el;  
2028     },
2029     
2030     getAutoCreate : function(){
2031          
2032         //if (['right'].indexOf(this.align)!==-1) {
2033         //    cfg.cn[1].cls += ' pull-right'
2034         //}
2035         
2036         
2037         var cfg = {
2038             tag : 'ul',
2039             cls : 'dropdown-menu' ,
2040             style : 'z-index:1000'
2041             
2042         };
2043         
2044         if (this.type === 'submenu') {
2045             cfg.cls = 'submenu active';
2046         }
2047         if (this.type === 'treeview') {
2048             cfg.cls = 'treeview-menu';
2049         }
2050         
2051         return cfg;
2052     },
2053     initEvents : function() {
2054         
2055        // Roo.log("ADD event");
2056        // Roo.log(this.triggerEl.dom);
2057         
2058         this.triggerEl.on('click', this.onTriggerClick, this);
2059         
2060         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2061         
2062         this.triggerEl.addClass('dropdown-toggle');
2063         
2064         if (Roo.isTouch) {
2065             this.el.on('touchstart'  , this.onTouch, this);
2066         }
2067         this.el.on('click' , this.onClick, this);
2068
2069         this.el.on("mouseover", this.onMouseOver, this);
2070         this.el.on("mouseout", this.onMouseOut, this);
2071         
2072     },
2073     
2074     findTargetItem : function(e)
2075     {
2076         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2077         if(!t){
2078             return false;
2079         }
2080         //Roo.log(t);         Roo.log(t.id);
2081         if(t && t.id){
2082             //Roo.log(this.menuitems);
2083             return this.menuitems.get(t.id);
2084             
2085             //return this.items.get(t.menuItemId);
2086         }
2087         
2088         return false;
2089     },
2090     
2091     onTouch : function(e) 
2092     {
2093         Roo.log("menu.onTouch");
2094         //e.stopEvent(); this make the user popdown broken
2095         this.onClick(e);
2096     },
2097     
2098     onClick : function(e)
2099     {
2100         Roo.log("menu.onClick");
2101         
2102         var t = this.findTargetItem(e);
2103         if(!t || t.isContainer){
2104             return;
2105         }
2106         Roo.log(e);
2107         /*
2108         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2109             if(t == this.activeItem && t.shouldDeactivate(e)){
2110                 this.activeItem.deactivate();
2111                 delete this.activeItem;
2112                 return;
2113             }
2114             if(t.canActivate){
2115                 this.setActiveItem(t, true);
2116             }
2117             return;
2118             
2119             
2120         }
2121         */
2122        
2123         Roo.log('pass click event');
2124         
2125         t.onClick(e);
2126         
2127         this.fireEvent("click", this, t, e);
2128         
2129         var _this = this;
2130         
2131         if(!t.href.length || t.href == '#'){
2132             (function() { _this.hide(); }).defer(100);
2133         }
2134         
2135     },
2136     
2137     onMouseOver : function(e){
2138         var t  = this.findTargetItem(e);
2139         //Roo.log(t);
2140         //if(t){
2141         //    if(t.canActivate && !t.disabled){
2142         //        this.setActiveItem(t, true);
2143         //    }
2144         //}
2145         
2146         this.fireEvent("mouseover", this, e, t);
2147     },
2148     isVisible : function(){
2149         return !this.hidden;
2150     },
2151      onMouseOut : function(e){
2152         var t  = this.findTargetItem(e);
2153         
2154         //if(t ){
2155         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2156         //        this.activeItem.deactivate();
2157         //        delete this.activeItem;
2158         //    }
2159         //}
2160         this.fireEvent("mouseout", this, e, t);
2161     },
2162     
2163     
2164     /**
2165      * Displays this menu relative to another element
2166      * @param {String/HTMLElement/Roo.Element} element The element to align to
2167      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2168      * the element (defaults to this.defaultAlign)
2169      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2170      */
2171     show : function(el, pos, parentMenu){
2172         this.parentMenu = parentMenu;
2173         if(!this.el){
2174             this.render();
2175         }
2176         this.fireEvent("beforeshow", this);
2177         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2178     },
2179      /**
2180      * Displays this menu at a specific xy position
2181      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2182      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2183      */
2184     showAt : function(xy, parentMenu, /* private: */_e){
2185         this.parentMenu = parentMenu;
2186         if(!this.el){
2187             this.render();
2188         }
2189         if(_e !== false){
2190             this.fireEvent("beforeshow", this);
2191             //xy = this.el.adjustForConstraints(xy);
2192         }
2193         
2194         //this.el.show();
2195         this.hideMenuItems();
2196         this.hidden = false;
2197         this.triggerEl.addClass('open');
2198         
2199         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2200             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2201         }
2202         
2203         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2204             this.el.setXY(xy);
2205         }
2206         
2207         this.focus();
2208         this.fireEvent("show", this);
2209     },
2210     
2211     focus : function(){
2212         return;
2213         if(!this.hidden){
2214             this.doFocus.defer(50, this);
2215         }
2216     },
2217
2218     doFocus : function(){
2219         if(!this.hidden){
2220             this.focusEl.focus();
2221         }
2222     },
2223
2224     /**
2225      * Hides this menu and optionally all parent menus
2226      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2227      */
2228     hide : function(deep)
2229     {
2230         
2231         this.hideMenuItems();
2232         if(this.el && this.isVisible()){
2233             this.fireEvent("beforehide", this);
2234             if(this.activeItem){
2235                 this.activeItem.deactivate();
2236                 this.activeItem = null;
2237             }
2238             this.triggerEl.removeClass('open');;
2239             this.hidden = true;
2240             this.fireEvent("hide", this);
2241         }
2242         if(deep === true && this.parentMenu){
2243             this.parentMenu.hide(true);
2244         }
2245     },
2246     
2247     onTriggerClick : function(e)
2248     {
2249         Roo.log('trigger click');
2250         
2251         var target = e.getTarget();
2252         
2253         Roo.log(target.nodeName.toLowerCase());
2254         
2255         if(target.nodeName.toLowerCase() === 'i'){
2256             e.preventDefault();
2257         }
2258         
2259     },
2260     
2261     onTriggerPress  : function(e)
2262     {
2263         Roo.log('trigger press');
2264         //Roo.log(e.getTarget());
2265        // Roo.log(this.triggerEl.dom);
2266        
2267         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2268         var pel = Roo.get(e.getTarget());
2269         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2270             Roo.log('is treeview or dropdown?');
2271             return;
2272         }
2273         
2274         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2275             return;
2276         }
2277         
2278         if (this.isVisible()) {
2279             Roo.log('hide');
2280             this.hide();
2281         } else {
2282             Roo.log('show');
2283             this.show(this.triggerEl, false, false);
2284         }
2285         
2286         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2287             e.stopEvent();
2288         }
2289         
2290     },
2291        
2292     
2293     hideMenuItems : function()
2294     {
2295         Roo.log("hide Menu Items");
2296         if (!this.el) { 
2297             return;
2298         }
2299         //$(backdrop).remove()
2300         this.el.select('.open',true).each(function(aa) {
2301             
2302             aa.removeClass('open');
2303           //var parent = getParent($(this))
2304           //var relatedTarget = { relatedTarget: this }
2305           
2306            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2307           //if (e.isDefaultPrevented()) return
2308            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2309         });
2310     },
2311     addxtypeChild : function (tree, cntr) {
2312         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2313           
2314         this.menuitems.add(comp);
2315         return comp;
2316
2317     },
2318     getEl : function()
2319     {
2320         Roo.log(this.el);
2321         return this.el;
2322     }
2323 });
2324
2325  
2326  /*
2327  * - LGPL
2328  *
2329  * menu item
2330  * 
2331  */
2332
2333
2334 /**
2335  * @class Roo.bootstrap.MenuItem
2336  * @extends Roo.bootstrap.Component
2337  * Bootstrap MenuItem class
2338  * @cfg {String} html the menu label
2339  * @cfg {String} href the link
2340  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2341  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2342  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2343  * @cfg {String} fa favicon to show on left of menu item.
2344  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2345  * 
2346  * 
2347  * @constructor
2348  * Create a new MenuItem
2349  * @param {Object} config The config object
2350  */
2351
2352
2353 Roo.bootstrap.MenuItem = function(config){
2354     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2355     this.addEvents({
2356         // raw events
2357         /**
2358          * @event click
2359          * The raw click event for the entire grid.
2360          * @param {Roo.bootstrap.MenuItem} this
2361          * @param {Roo.EventObject} e
2362          */
2363         "click" : true
2364     });
2365 };
2366
2367 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2368     
2369     href : false,
2370     html : false,
2371     preventDefault: false,
2372     isContainer : false,
2373     active : false,
2374     fa: false,
2375     
2376     getAutoCreate : function(){
2377         
2378         if(this.isContainer){
2379             return {
2380                 tag: 'li',
2381                 cls: 'dropdown-menu-item'
2382             };
2383         }
2384         var ctag = {
2385             tag: 'span',
2386             html: 'Link'
2387         };
2388         
2389         var anc = {
2390             tag : 'a',
2391             href : '#',
2392             cn : [  ]
2393         };
2394         
2395         if (this.fa !== false) {
2396             anc.cn.push({
2397                 tag : 'i',
2398                 cls : 'fa fa-' + this.fa
2399             });
2400         }
2401         
2402         anc.cn.push(ctag);
2403         
2404         
2405         var cfg= {
2406             tag: 'li',
2407             cls: 'dropdown-menu-item',
2408             cn: [ anc ]
2409         };
2410         if (this.parent().type == 'treeview') {
2411             cfg.cls = 'treeview-menu';
2412         }
2413         if (this.active) {
2414             cfg.cls += ' active';
2415         }
2416         
2417         
2418         
2419         anc.href = this.href || cfg.cn[0].href ;
2420         ctag.html = this.html || cfg.cn[0].html ;
2421         return cfg;
2422     },
2423     
2424     initEvents: function()
2425     {
2426         if (this.parent().type == 'treeview') {
2427             this.el.select('a').on('click', this.onClick, this);
2428         }
2429         
2430         if (this.menu) {
2431             this.menu.parentType = this.xtype;
2432             this.menu.triggerEl = this.el;
2433             this.menu = this.addxtype(Roo.apply({}, this.menu));
2434         }
2435         
2436     },
2437     onClick : function(e)
2438     {
2439         Roo.log('item on click ');
2440         
2441         if(this.preventDefault){
2442             e.preventDefault();
2443         }
2444         //this.parent().hideMenuItems();
2445         
2446         this.fireEvent('click', this, e);
2447     },
2448     getEl : function()
2449     {
2450         return this.el;
2451     } 
2452 });
2453
2454  
2455
2456  /*
2457  * - LGPL
2458  *
2459  * menu separator
2460  * 
2461  */
2462
2463
2464 /**
2465  * @class Roo.bootstrap.MenuSeparator
2466  * @extends Roo.bootstrap.Component
2467  * Bootstrap MenuSeparator class
2468  * 
2469  * @constructor
2470  * Create a new MenuItem
2471  * @param {Object} config The config object
2472  */
2473
2474
2475 Roo.bootstrap.MenuSeparator = function(config){
2476     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2477 };
2478
2479 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2480     
2481     getAutoCreate : function(){
2482         var cfg = {
2483             cls: 'divider',
2484             tag : 'li'
2485         };
2486         
2487         return cfg;
2488     }
2489    
2490 });
2491
2492  
2493
2494  
2495 /*
2496 * Licence: LGPL
2497 */
2498
2499 /**
2500  * @class Roo.bootstrap.Modal
2501  * @extends Roo.bootstrap.Component
2502  * Bootstrap Modal class
2503  * @cfg {String} title Title of dialog
2504  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2505  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2506  * @cfg {Boolean} specificTitle default false
2507  * @cfg {Array} buttons Array of buttons or standard button set..
2508  * @cfg {String} buttonPosition (left|right|center) default right
2509  * @cfg {Boolean} animate default true
2510  * @cfg {Boolean} allow_close default true
2511  * @cfg {Boolean} fitwindow default false
2512  * @cfg {String} size (sm|lg) default empty
2513  *
2514  *
2515  * @constructor
2516  * Create a new Modal Dialog
2517  * @param {Object} config The config object
2518  */
2519
2520 Roo.bootstrap.Modal = function(config){
2521     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2522     this.addEvents({
2523         // raw events
2524         /**
2525          * @event btnclick
2526          * The raw btnclick event for the button
2527          * @param {Roo.EventObject} e
2528          */
2529         "btnclick" : true,
2530         /**
2531          * @event resize
2532          * Fire when dialog resize
2533          * @param {Roo.bootstrap.Modal} this
2534          * @param {Roo.EventObject} e
2535          */
2536         "resize" : true
2537     });
2538     this.buttons = this.buttons || [];
2539
2540     if (this.tmpl) {
2541         this.tmpl = Roo.factory(this.tmpl);
2542     }
2543
2544 };
2545
2546 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2547
2548     title : 'test dialog',
2549
2550     buttons : false,
2551
2552     // set on load...
2553
2554     html: false,
2555
2556     tmp: false,
2557
2558     specificTitle: false,
2559
2560     buttonPosition: 'right',
2561
2562     allow_close : true,
2563
2564     animate : true,
2565
2566     fitwindow: false,
2567
2568
2569      // private
2570     dialogEl: false,
2571     bodyEl:  false,
2572     footerEl:  false,
2573     titleEl:  false,
2574     closeEl:  false,
2575
2576     size: '',
2577
2578
2579     onRender : function(ct, position)
2580     {
2581         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2582
2583         if(!this.el){
2584             var cfg = Roo.apply({},  this.getAutoCreate());
2585             cfg.id = Roo.id();
2586             //if(!cfg.name){
2587             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2588             //}
2589             //if (!cfg.name.length) {
2590             //    delete cfg.name;
2591            // }
2592             if (this.cls) {
2593                 cfg.cls += ' ' + this.cls;
2594             }
2595             if (this.style) {
2596                 cfg.style = this.style;
2597             }
2598             this.el = Roo.get(document.body).createChild(cfg, position);
2599         }
2600         //var type = this.el.dom.type;
2601
2602
2603         if(this.tabIndex !== undefined){
2604             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2605         }
2606
2607         this.dialogEl = this.el.select('.modal-dialog',true).first();
2608         this.bodyEl = this.el.select('.modal-body',true).first();
2609         this.closeEl = this.el.select('.modal-header .close', true).first();
2610         this.headerEl = this.el.select('.modal-header',true).first();
2611         this.titleEl = this.el.select('.modal-title',true).first();
2612         this.footerEl = this.el.select('.modal-footer',true).first();
2613
2614         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2615         this.maskEl.enableDisplayMode("block");
2616         this.maskEl.hide();
2617         //this.el.addClass("x-dlg-modal");
2618
2619         if (this.buttons.length) {
2620             Roo.each(this.buttons, function(bb) {
2621                 var b = Roo.apply({}, bb);
2622                 b.xns = b.xns || Roo.bootstrap;
2623                 b.xtype = b.xtype || 'Button';
2624                 if (typeof(b.listeners) == 'undefined') {
2625                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2626                 }
2627
2628                 var btn = Roo.factory(b);
2629
2630                 btn.render(this.el.select('.modal-footer div').first());
2631
2632             },this);
2633         }
2634         // render the children.
2635         var nitems = [];
2636
2637         if(typeof(this.items) != 'undefined'){
2638             var items = this.items;
2639             delete this.items;
2640
2641             for(var i =0;i < items.length;i++) {
2642                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2643             }
2644         }
2645
2646         this.items = nitems;
2647
2648         // where are these used - they used to be body/close/footer
2649
2650
2651         this.initEvents();
2652         //this.el.addClass([this.fieldClass, this.cls]);
2653
2654     },
2655
2656     getAutoCreate : function(){
2657
2658
2659         var bdy = {
2660                 cls : 'modal-body',
2661                 html : this.html || ''
2662         };
2663
2664         var title = {
2665             tag: 'h4',
2666             cls : 'modal-title',
2667             html : this.title
2668         };
2669
2670         if(this.specificTitle){
2671             title = this.title;
2672
2673         };
2674
2675         var header = [];
2676         if (this.allow_close) {
2677             header.push({
2678                 tag: 'button',
2679                 cls : 'close',
2680                 html : '&times'
2681             });
2682         }
2683
2684         header.push(title);
2685
2686         var size = '';
2687
2688         if(this.size.length){
2689             size = 'modal-' + this.size;
2690         }
2691
2692         var modal = {
2693             cls: "modal",
2694             style : 'display: none',
2695             cn : [
2696                 {
2697                     cls: "modal-dialog " + size,
2698                     cn : [
2699                         {
2700                             cls : "modal-content",
2701                             cn : [
2702                                 {
2703                                     cls : 'modal-header',
2704                                     cn : header
2705                                 },
2706                                 bdy,
2707                                 {
2708                                     cls : 'modal-footer',
2709                                     cn : [
2710                                         {
2711                                             tag: 'div',
2712                                             cls: 'btn-' + this.buttonPosition
2713                                         }
2714                                     ]
2715
2716                                 }
2717
2718
2719                             ]
2720
2721                         }
2722                     ]
2723
2724                 }
2725             ]
2726         };
2727
2728         if(this.animate){
2729             modal.cls += ' fade';
2730         }
2731
2732         return modal;
2733
2734     },
2735     getChildContainer : function() {
2736
2737          return this.bodyEl;
2738
2739     },
2740     getButtonContainer : function() {
2741          return this.el.select('.modal-footer div',true).first();
2742
2743     },
2744     initEvents : function()
2745     {
2746         if (this.allow_close) {
2747             this.closeEl.on('click', this.hide, this);
2748         }
2749         Roo.EventManager.onWindowResize(this.resize, this, true);
2750
2751
2752     },
2753
2754     resize : function()
2755     {
2756         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2757         if (this.fitwindow) {
2758             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2759             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2760             this.setSize(w,h);
2761         }
2762     },
2763
2764     setSize : function(w,h)
2765     {
2766         if (!w && !h) {
2767             return;
2768         }
2769         this.resizeTo(w,h);
2770     },
2771
2772     show : function() {
2773
2774         if (!this.rendered) {
2775             this.render();
2776         }
2777
2778         this.el.setStyle('display', 'block');
2779
2780         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2781             var _this = this;
2782             (function(){
2783                 this.el.addClass('in');
2784             }).defer(50, this);
2785         }else{
2786             this.el.addClass('in');
2787
2788         }
2789
2790         // not sure how we can show data in here..
2791         //if (this.tmpl) {
2792         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2793         //}
2794
2795         Roo.get(document.body).addClass("x-body-masked");
2796         
2797         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2798         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2799         this.maskEl.show();
2800         
2801         this.resize();
2802         
2803         this.fireEvent('show', this);
2804
2805         // set zindex here - otherwise it appears to be ignored...
2806         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2807
2808         (function () {
2809             this.items.forEach( function(e) {
2810                 e.layout ? e.layout() : false;
2811
2812             });
2813         }).defer(100,this);
2814
2815     },
2816     hide : function()
2817     {
2818         if(this.fireEvent("beforehide", this) !== false){
2819             this.maskEl.hide();
2820             Roo.get(document.body).removeClass("x-body-masked");
2821             this.el.removeClass('in');
2822             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2823
2824             if(this.animate){ // why
2825                 var _this = this;
2826                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2827             }else{
2828                 this.el.setStyle('display', 'none');
2829             }
2830             this.fireEvent('hide', this);
2831         }
2832     },
2833
2834     addButton : function(str, cb)
2835     {
2836
2837
2838         var b = Roo.apply({}, { html : str } );
2839         b.xns = b.xns || Roo.bootstrap;
2840         b.xtype = b.xtype || 'Button';
2841         if (typeof(b.listeners) == 'undefined') {
2842             b.listeners = { click : cb.createDelegate(this)  };
2843         }
2844
2845         var btn = Roo.factory(b);
2846
2847         btn.render(this.el.select('.modal-footer div').first());
2848
2849         return btn;
2850
2851     },
2852
2853     setDefaultButton : function(btn)
2854     {
2855         //this.el.select('.modal-footer').()
2856     },
2857     diff : false,
2858
2859     resizeTo: function(w,h)
2860     {
2861         // skip.. ?? why??
2862
2863         this.dialogEl.setWidth(w);
2864         if (this.diff === false) {
2865             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2866         }
2867
2868         this.bodyEl.setHeight(h-this.diff);
2869
2870         this.fireEvent('resize', this);
2871
2872     },
2873     setContentSize  : function(w, h)
2874     {
2875
2876     },
2877     onButtonClick: function(btn,e)
2878     {
2879         //Roo.log([a,b,c]);
2880         this.fireEvent('btnclick', btn.name, e);
2881     },
2882      /**
2883      * Set the title of the Dialog
2884      * @param {String} str new Title
2885      */
2886     setTitle: function(str) {
2887         this.titleEl.dom.innerHTML = str;
2888     },
2889     /**
2890      * Set the body of the Dialog
2891      * @param {String} str new Title
2892      */
2893     setBody: function(str) {
2894         this.bodyEl.dom.innerHTML = str;
2895     },
2896     /**
2897      * Set the body of the Dialog using the template
2898      * @param {Obj} data - apply this data to the template and replace the body contents.
2899      */
2900     applyBody: function(obj)
2901     {
2902         if (!this.tmpl) {
2903             Roo.log("Error - using apply Body without a template");
2904             //code
2905         }
2906         this.tmpl.overwrite(this.bodyEl, obj);
2907     }
2908
2909 });
2910
2911
2912 Roo.apply(Roo.bootstrap.Modal,  {
2913     /**
2914          * Button config that displays a single OK button
2915          * @type Object
2916          */
2917         OK :  [{
2918             name : 'ok',
2919             weight : 'primary',
2920             html : 'OK'
2921         }],
2922         /**
2923          * Button config that displays Yes and No buttons
2924          * @type Object
2925          */
2926         YESNO : [
2927             {
2928                 name  : 'no',
2929                 html : 'No'
2930             },
2931             {
2932                 name  :'yes',
2933                 weight : 'primary',
2934                 html : 'Yes'
2935             }
2936         ],
2937
2938         /**
2939          * Button config that displays OK and Cancel buttons
2940          * @type Object
2941          */
2942         OKCANCEL : [
2943             {
2944                name : 'cancel',
2945                 html : 'Cancel'
2946             },
2947             {
2948                 name : 'ok',
2949                 weight : 'primary',
2950                 html : 'OK'
2951             }
2952         ],
2953         /**
2954          * Button config that displays Yes, No and Cancel buttons
2955          * @type Object
2956          */
2957         YESNOCANCEL : [
2958             {
2959                 name : 'yes',
2960                 weight : 'primary',
2961                 html : 'Yes'
2962             },
2963             {
2964                 name : 'no',
2965                 html : 'No'
2966             },
2967             {
2968                 name : 'cancel',
2969                 html : 'Cancel'
2970             }
2971         ],
2972         
2973         zIndex : 10001
2974 });
2975 /*
2976  * - LGPL
2977  *
2978  * messagebox - can be used as a replace
2979  * 
2980  */
2981 /**
2982  * @class Roo.MessageBox
2983  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2984  * Example usage:
2985  *<pre><code>
2986 // Basic alert:
2987 Roo.Msg.alert('Status', 'Changes saved successfully.');
2988
2989 // Prompt for user data:
2990 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2991     if (btn == 'ok'){
2992         // process text value...
2993     }
2994 });
2995
2996 // Show a dialog using config options:
2997 Roo.Msg.show({
2998    title:'Save Changes?',
2999    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3000    buttons: Roo.Msg.YESNOCANCEL,
3001    fn: processResult,
3002    animEl: 'elId'
3003 });
3004 </code></pre>
3005  * @singleton
3006  */
3007 Roo.bootstrap.MessageBox = function(){
3008     var dlg, opt, mask, waitTimer;
3009     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3010     var buttons, activeTextEl, bwidth;
3011
3012     
3013     // private
3014     var handleButton = function(button){
3015         dlg.hide();
3016         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3017     };
3018
3019     // private
3020     var handleHide = function(){
3021         if(opt && opt.cls){
3022             dlg.el.removeClass(opt.cls);
3023         }
3024         //if(waitTimer){
3025         //    Roo.TaskMgr.stop(waitTimer);
3026         //    waitTimer = null;
3027         //}
3028     };
3029
3030     // private
3031     var updateButtons = function(b){
3032         var width = 0;
3033         if(!b){
3034             buttons["ok"].hide();
3035             buttons["cancel"].hide();
3036             buttons["yes"].hide();
3037             buttons["no"].hide();
3038             //dlg.footer.dom.style.display = 'none';
3039             return width;
3040         }
3041         dlg.footerEl.dom.style.display = '';
3042         for(var k in buttons){
3043             if(typeof buttons[k] != "function"){
3044                 if(b[k]){
3045                     buttons[k].show();
3046                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3047                     width += buttons[k].el.getWidth()+15;
3048                 }else{
3049                     buttons[k].hide();
3050                 }
3051             }
3052         }
3053         return width;
3054     };
3055
3056     // private
3057     var handleEsc = function(d, k, e){
3058         if(opt && opt.closable !== false){
3059             dlg.hide();
3060         }
3061         if(e){
3062             e.stopEvent();
3063         }
3064     };
3065
3066     return {
3067         /**
3068          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3069          * @return {Roo.BasicDialog} The BasicDialog element
3070          */
3071         getDialog : function(){
3072            if(!dlg){
3073                 dlg = new Roo.bootstrap.Modal( {
3074                     //draggable: true,
3075                     //resizable:false,
3076                     //constraintoviewport:false,
3077                     //fixedcenter:true,
3078                     //collapsible : false,
3079                     //shim:true,
3080                     //modal: true,
3081                 //    width: 'auto',
3082                   //  height:100,
3083                     //buttonAlign:"center",
3084                     closeClick : function(){
3085                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3086                             handleButton("no");
3087                         }else{
3088                             handleButton("cancel");
3089                         }
3090                     }
3091                 });
3092                 dlg.render();
3093                 dlg.on("hide", handleHide);
3094                 mask = dlg.mask;
3095                 //dlg.addKeyListener(27, handleEsc);
3096                 buttons = {};
3097                 this.buttons = buttons;
3098                 var bt = this.buttonText;
3099                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3100                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3101                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3102                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3103                 //Roo.log(buttons);
3104                 bodyEl = dlg.bodyEl.createChild({
3105
3106                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3107                         '<textarea class="roo-mb-textarea"></textarea>' +
3108                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3109                 });
3110                 msgEl = bodyEl.dom.firstChild;
3111                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3112                 textboxEl.enableDisplayMode();
3113                 textboxEl.addKeyListener([10,13], function(){
3114                     if(dlg.isVisible() && opt && opt.buttons){
3115                         if(opt.buttons.ok){
3116                             handleButton("ok");
3117                         }else if(opt.buttons.yes){
3118                             handleButton("yes");
3119                         }
3120                     }
3121                 });
3122                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3123                 textareaEl.enableDisplayMode();
3124                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3125                 progressEl.enableDisplayMode();
3126                 
3127                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3128                 var pf = progressEl.dom.firstChild;
3129                 if (pf) {
3130                     pp = Roo.get(pf.firstChild);
3131                     pp.setHeight(pf.offsetHeight);
3132                 }
3133                 
3134             }
3135             return dlg;
3136         },
3137
3138         /**
3139          * Updates the message box body text
3140          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3141          * the XHTML-compliant non-breaking space character '&amp;#160;')
3142          * @return {Roo.MessageBox} This message box
3143          */
3144         updateText : function(text)
3145         {
3146             if(!dlg.isVisible() && !opt.width){
3147                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3148                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3149             }
3150             msgEl.innerHTML = text || '&#160;';
3151       
3152             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3153             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3154             var w = Math.max(
3155                     Math.min(opt.width || cw , this.maxWidth), 
3156                     Math.max(opt.minWidth || this.minWidth, bwidth)
3157             );
3158             if(opt.prompt){
3159                 activeTextEl.setWidth(w);
3160             }
3161             if(dlg.isVisible()){
3162                 dlg.fixedcenter = false;
3163             }
3164             // to big, make it scroll. = But as usual stupid IE does not support
3165             // !important..
3166             
3167             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3168                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3169                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3170             } else {
3171                 bodyEl.dom.style.height = '';
3172                 bodyEl.dom.style.overflowY = '';
3173             }
3174             if (cw > w) {
3175                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3176             } else {
3177                 bodyEl.dom.style.overflowX = '';
3178             }
3179             
3180             dlg.setContentSize(w, bodyEl.getHeight());
3181             if(dlg.isVisible()){
3182                 dlg.fixedcenter = true;
3183             }
3184             return this;
3185         },
3186
3187         /**
3188          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3189          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3190          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3191          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3192          * @return {Roo.MessageBox} This message box
3193          */
3194         updateProgress : function(value, text){
3195             if(text){
3196                 this.updateText(text);
3197             }
3198             
3199             if (pp) { // weird bug on my firefox - for some reason this is not defined
3200                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3201                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3202             }
3203             return this;
3204         },        
3205
3206         /**
3207          * Returns true if the message box is currently displayed
3208          * @return {Boolean} True if the message box is visible, else false
3209          */
3210         isVisible : function(){
3211             return dlg && dlg.isVisible();  
3212         },
3213
3214         /**
3215          * Hides the message box if it is displayed
3216          */
3217         hide : function(){
3218             if(this.isVisible()){
3219                 dlg.hide();
3220             }  
3221         },
3222
3223         /**
3224          * Displays a new message box, or reinitializes an existing message box, based on the config options
3225          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3226          * The following config object properties are supported:
3227          * <pre>
3228 Property    Type             Description
3229 ----------  ---------------  ------------------------------------------------------------------------------------
3230 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3231                                    closes (defaults to undefined)
3232 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3233                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3234 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3235                                    progress and wait dialogs will ignore this property and always hide the
3236                                    close button as they can only be closed programmatically.
3237 cls               String           A custom CSS class to apply to the message box element
3238 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3239                                    displayed (defaults to 75)
3240 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3241                                    function will be btn (the name of the button that was clicked, if applicable,
3242                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3243                                    Progress and wait dialogs will ignore this option since they do not respond to
3244                                    user actions and can only be closed programmatically, so any required function
3245                                    should be called by the same code after it closes the dialog.
3246 icon              String           A CSS class that provides a background image to be used as an icon for
3247                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3248 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3249 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3250 modal             Boolean          False to allow user interaction with the page while the message box is
3251                                    displayed (defaults to true)
3252 msg               String           A string that will replace the existing message box body text (defaults
3253                                    to the XHTML-compliant non-breaking space character '&#160;')
3254 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3255 progress          Boolean          True to display a progress bar (defaults to false)
3256 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3257 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3258 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3259 title             String           The title text
3260 value             String           The string value to set into the active textbox element if displayed
3261 wait              Boolean          True to display a progress bar (defaults to false)
3262 width             Number           The width of the dialog in pixels
3263 </pre>
3264          *
3265          * Example usage:
3266          * <pre><code>
3267 Roo.Msg.show({
3268    title: 'Address',
3269    msg: 'Please enter your address:',
3270    width: 300,
3271    buttons: Roo.MessageBox.OKCANCEL,
3272    multiline: true,
3273    fn: saveAddress,
3274    animEl: 'addAddressBtn'
3275 });
3276 </code></pre>
3277          * @param {Object} config Configuration options
3278          * @return {Roo.MessageBox} This message box
3279          */
3280         show : function(options)
3281         {
3282             
3283             // this causes nightmares if you show one dialog after another
3284             // especially on callbacks..
3285              
3286             if(this.isVisible()){
3287                 
3288                 this.hide();
3289                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3290                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3291                 Roo.log("New Dialog Message:" +  options.msg )
3292                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3293                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3294                 
3295             }
3296             var d = this.getDialog();
3297             opt = options;
3298             d.setTitle(opt.title || "&#160;");
3299             d.closeEl.setDisplayed(opt.closable !== false);
3300             activeTextEl = textboxEl;
3301             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3302             if(opt.prompt){
3303                 if(opt.multiline){
3304                     textboxEl.hide();
3305                     textareaEl.show();
3306                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3307                         opt.multiline : this.defaultTextHeight);
3308                     activeTextEl = textareaEl;
3309                 }else{
3310                     textboxEl.show();
3311                     textareaEl.hide();
3312                 }
3313             }else{
3314                 textboxEl.hide();
3315                 textareaEl.hide();
3316             }
3317             progressEl.setDisplayed(opt.progress === true);
3318             this.updateProgress(0);
3319             activeTextEl.dom.value = opt.value || "";
3320             if(opt.prompt){
3321                 dlg.setDefaultButton(activeTextEl);
3322             }else{
3323                 var bs = opt.buttons;
3324                 var db = null;
3325                 if(bs && bs.ok){
3326                     db = buttons["ok"];
3327                 }else if(bs && bs.yes){
3328                     db = buttons["yes"];
3329                 }
3330                 dlg.setDefaultButton(db);
3331             }
3332             bwidth = updateButtons(opt.buttons);
3333             this.updateText(opt.msg);
3334             if(opt.cls){
3335                 d.el.addClass(opt.cls);
3336             }
3337             d.proxyDrag = opt.proxyDrag === true;
3338             d.modal = opt.modal !== false;
3339             d.mask = opt.modal !== false ? mask : false;
3340             if(!d.isVisible()){
3341                 // force it to the end of the z-index stack so it gets a cursor in FF
3342                 document.body.appendChild(dlg.el.dom);
3343                 d.animateTarget = null;
3344                 d.show(options.animEl);
3345             }
3346             return this;
3347         },
3348
3349         /**
3350          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3351          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3352          * and closing the message box when the process is complete.
3353          * @param {String} title The title bar text
3354          * @param {String} msg The message box body text
3355          * @return {Roo.MessageBox} This message box
3356          */
3357         progress : function(title, msg){
3358             this.show({
3359                 title : title,
3360                 msg : msg,
3361                 buttons: false,
3362                 progress:true,
3363                 closable:false,
3364                 minWidth: this.minProgressWidth,
3365                 modal : true
3366             });
3367             return this;
3368         },
3369
3370         /**
3371          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3372          * If a callback function is passed it will be called after the user clicks the button, and the
3373          * id of the button that was clicked will be passed as the only parameter to the callback
3374          * (could also be the top-right close button).
3375          * @param {String} title The title bar text
3376          * @param {String} msg The message box body text
3377          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3378          * @param {Object} scope (optional) The scope of the callback function
3379          * @return {Roo.MessageBox} This message box
3380          */
3381         alert : function(title, msg, fn, scope)
3382         {
3383             this.show({
3384                 title : title,
3385                 msg : msg,
3386                 buttons: this.OK,
3387                 fn: fn,
3388                 closable : false,
3389                 scope : scope,
3390                 modal : true
3391             });
3392             return this;
3393         },
3394
3395         /**
3396          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3397          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3398          * You are responsible for closing the message box when the process is complete.
3399          * @param {String} msg The message box body text
3400          * @param {String} title (optional) The title bar text
3401          * @return {Roo.MessageBox} This message box
3402          */
3403         wait : function(msg, title){
3404             this.show({
3405                 title : title,
3406                 msg : msg,
3407                 buttons: false,
3408                 closable:false,
3409                 progress:true,
3410                 modal:true,
3411                 width:300,
3412                 wait:true
3413             });
3414             waitTimer = Roo.TaskMgr.start({
3415                 run: function(i){
3416                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3417                 },
3418                 interval: 1000
3419             });
3420             return this;
3421         },
3422
3423         /**
3424          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3425          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3426          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3427          * @param {String} title The title bar text
3428          * @param {String} msg The message box body text
3429          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3430          * @param {Object} scope (optional) The scope of the callback function
3431          * @return {Roo.MessageBox} This message box
3432          */
3433         confirm : function(title, msg, fn, scope){
3434             this.show({
3435                 title : title,
3436                 msg : msg,
3437                 buttons: this.YESNO,
3438                 fn: fn,
3439                 scope : scope,
3440                 modal : true
3441             });
3442             return this;
3443         },
3444
3445         /**
3446          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3447          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3448          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3449          * (could also be the top-right close button) and the text that was entered will be passed as the two
3450          * parameters to the callback.
3451          * @param {String} title The title bar text
3452          * @param {String} msg The message box body text
3453          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3454          * @param {Object} scope (optional) The scope of the callback function
3455          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3456          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3457          * @return {Roo.MessageBox} This message box
3458          */
3459         prompt : function(title, msg, fn, scope, multiline){
3460             this.show({
3461                 title : title,
3462                 msg : msg,
3463                 buttons: this.OKCANCEL,
3464                 fn: fn,
3465                 minWidth:250,
3466                 scope : scope,
3467                 prompt:true,
3468                 multiline: multiline,
3469                 modal : true
3470             });
3471             return this;
3472         },
3473
3474         /**
3475          * Button config that displays a single OK button
3476          * @type Object
3477          */
3478         OK : {ok:true},
3479         /**
3480          * Button config that displays Yes and No buttons
3481          * @type Object
3482          */
3483         YESNO : {yes:true, no:true},
3484         /**
3485          * Button config that displays OK and Cancel buttons
3486          * @type Object
3487          */
3488         OKCANCEL : {ok:true, cancel:true},
3489         /**
3490          * Button config that displays Yes, No and Cancel buttons
3491          * @type Object
3492          */
3493         YESNOCANCEL : {yes:true, no:true, cancel:true},
3494
3495         /**
3496          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3497          * @type Number
3498          */
3499         defaultTextHeight : 75,
3500         /**
3501          * The maximum width in pixels of the message box (defaults to 600)
3502          * @type Number
3503          */
3504         maxWidth : 600,
3505         /**
3506          * The minimum width in pixels of the message box (defaults to 100)
3507          * @type Number
3508          */
3509         minWidth : 100,
3510         /**
3511          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3512          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3513          * @type Number
3514          */
3515         minProgressWidth : 250,
3516         /**
3517          * An object containing the default button text strings that can be overriden for localized language support.
3518          * Supported properties are: ok, cancel, yes and no.
3519          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3520          * @type Object
3521          */
3522         buttonText : {
3523             ok : "OK",
3524             cancel : "Cancel",
3525             yes : "Yes",
3526             no : "No"
3527         }
3528     };
3529 }();
3530
3531 /**
3532  * Shorthand for {@link Roo.MessageBox}
3533  */
3534 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3535 Roo.Msg = Roo.Msg || Roo.MessageBox;
3536 /*
3537  * - LGPL
3538  *
3539  * navbar
3540  * 
3541  */
3542
3543 /**
3544  * @class Roo.bootstrap.Navbar
3545  * @extends Roo.bootstrap.Component
3546  * Bootstrap Navbar class
3547
3548  * @constructor
3549  * Create a new Navbar
3550  * @param {Object} config The config object
3551  */
3552
3553
3554 Roo.bootstrap.Navbar = function(config){
3555     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3556     this.addEvents({
3557         // raw events
3558         /**
3559          * @event beforetoggle
3560          * Fire before toggle the menu
3561          * @param {Roo.EventObject} e
3562          */
3563         "beforetoggle" : true
3564     });
3565 };
3566
3567 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3568     
3569     
3570    
3571     // private
3572     navItems : false,
3573     loadMask : false,
3574     
3575     
3576     getAutoCreate : function(){
3577         
3578         
3579         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3580         
3581     },
3582     
3583     initEvents :function ()
3584     {
3585         //Roo.log(this.el.select('.navbar-toggle',true));
3586         this.el.select('.navbar-toggle',true).on('click', function() {
3587             if(this.fireEvent('beforetoggle', this) !== false){
3588                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3589             }
3590             
3591         }, this);
3592         
3593         var mark = {
3594             tag: "div",
3595             cls:"x-dlg-mask"
3596         };
3597         
3598         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3599         
3600         var size = this.el.getSize();
3601         this.maskEl.setSize(size.width, size.height);
3602         this.maskEl.enableDisplayMode("block");
3603         this.maskEl.hide();
3604         
3605         if(this.loadMask){
3606             this.maskEl.show();
3607         }
3608     },
3609     
3610     
3611     getChildContainer : function()
3612     {
3613         if (this.el.select('.collapse').getCount()) {
3614             return this.el.select('.collapse',true).first();
3615         }
3616         
3617         return this.el;
3618     },
3619     
3620     mask : function()
3621     {
3622         this.maskEl.show();
3623     },
3624     
3625     unmask : function()
3626     {
3627         this.maskEl.hide();
3628     } 
3629     
3630     
3631     
3632     
3633 });
3634
3635
3636
3637  
3638
3639  /*
3640  * - LGPL
3641  *
3642  * navbar
3643  * 
3644  */
3645
3646 /**
3647  * @class Roo.bootstrap.NavSimplebar
3648  * @extends Roo.bootstrap.Navbar
3649  * Bootstrap Sidebar class
3650  *
3651  * @cfg {Boolean} inverse is inverted color
3652  * 
3653  * @cfg {String} type (nav | pills | tabs)
3654  * @cfg {Boolean} arrangement stacked | justified
3655  * @cfg {String} align (left | right) alignment
3656  * 
3657  * @cfg {Boolean} main (true|false) main nav bar? default false
3658  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3659  * 
3660  * @cfg {String} tag (header|footer|nav|div) default is nav 
3661
3662  * 
3663  * 
3664  * 
3665  * @constructor
3666  * Create a new Sidebar
3667  * @param {Object} config The config object
3668  */
3669
3670
3671 Roo.bootstrap.NavSimplebar = function(config){
3672     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3673 };
3674
3675 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3676     
3677     inverse: false,
3678     
3679     type: false,
3680     arrangement: '',
3681     align : false,
3682     
3683     
3684     
3685     main : false,
3686     
3687     
3688     tag : false,
3689     
3690     
3691     getAutoCreate : function(){
3692         
3693         
3694         var cfg = {
3695             tag : this.tag || 'div',
3696             cls : 'navbar'
3697         };
3698           
3699         
3700         cfg.cn = [
3701             {
3702                 cls: 'nav',
3703                 tag : 'ul'
3704             }
3705         ];
3706         
3707          
3708         this.type = this.type || 'nav';
3709         if (['tabs','pills'].indexOf(this.type)!==-1) {
3710             cfg.cn[0].cls += ' nav-' + this.type
3711         
3712         
3713         } else {
3714             if (this.type!=='nav') {
3715                 Roo.log('nav type must be nav/tabs/pills')
3716             }
3717             cfg.cn[0].cls += ' navbar-nav'
3718         }
3719         
3720         
3721         
3722         
3723         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3724             cfg.cn[0].cls += ' nav-' + this.arrangement;
3725         }
3726         
3727         
3728         if (this.align === 'right') {
3729             cfg.cn[0].cls += ' navbar-right';
3730         }
3731         
3732         if (this.inverse) {
3733             cfg.cls += ' navbar-inverse';
3734             
3735         }
3736         
3737         
3738         return cfg;
3739     
3740         
3741     }
3742     
3743     
3744     
3745 });
3746
3747
3748
3749  
3750
3751  
3752        /*
3753  * - LGPL
3754  *
3755  * navbar
3756  * 
3757  */
3758
3759 /**
3760  * @class Roo.bootstrap.NavHeaderbar
3761  * @extends Roo.bootstrap.NavSimplebar
3762  * Bootstrap Sidebar class
3763  *
3764  * @cfg {String} brand what is brand
3765  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3766  * @cfg {String} brand_href href of the brand
3767  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3768  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3769  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3770  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3771  * 
3772  * @constructor
3773  * Create a new Sidebar
3774  * @param {Object} config The config object
3775  */
3776
3777
3778 Roo.bootstrap.NavHeaderbar = function(config){
3779     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3780       
3781 };
3782
3783 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3784     
3785     position: '',
3786     brand: '',
3787     brand_href: false,
3788     srButton : true,
3789     autohide : false,
3790     desktopCenter : false,
3791    
3792     
3793     getAutoCreate : function(){
3794         
3795         var   cfg = {
3796             tag: this.nav || 'nav',
3797             cls: 'navbar',
3798             role: 'navigation',
3799             cn: []
3800         };
3801         
3802         var cn = cfg.cn;
3803         if (this.desktopCenter) {
3804             cn.push({cls : 'container', cn : []});
3805             cn = cn[0].cn;
3806         }
3807         
3808         if(this.srButton){
3809             cn.push({
3810                 tag: 'div',
3811                 cls: 'navbar-header',
3812                 cn: [
3813                     {
3814                         tag: 'button',
3815                         type: 'button',
3816                         cls: 'navbar-toggle',
3817                         'data-toggle': 'collapse',
3818                         cn: [
3819                             {
3820                                 tag: 'span',
3821                                 cls: 'sr-only',
3822                                 html: 'Toggle navigation'
3823                             },
3824                             {
3825                                 tag: 'span',
3826                                 cls: 'icon-bar'
3827                             },
3828                             {
3829                                 tag: 'span',
3830                                 cls: 'icon-bar'
3831                             },
3832                             {
3833                                 tag: 'span',
3834                                 cls: 'icon-bar'
3835                             }
3836                         ]
3837                     }
3838                 ]
3839             });
3840         }
3841         
3842         cn.push({
3843             tag: 'div',
3844             cls: 'collapse navbar-collapse',
3845             cn : []
3846         });
3847         
3848         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3849         
3850         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3851             cfg.cls += ' navbar-' + this.position;
3852             
3853             // tag can override this..
3854             
3855             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3856         }
3857         
3858         if (this.brand !== '') {
3859             cn[0].cn.push({
3860                 tag: 'a',
3861                 href: this.brand_href ? this.brand_href : '#',
3862                 cls: 'navbar-brand',
3863                 cn: [
3864                 this.brand
3865                 ]
3866             });
3867         }
3868         
3869         if(this.main){
3870             cfg.cls += ' main-nav';
3871         }
3872         
3873         
3874         return cfg;
3875
3876         
3877     },
3878     getHeaderChildContainer : function()
3879     {
3880         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3881             return this.el.select('.navbar-header',true).first();
3882         }
3883         
3884         return this.getChildContainer();
3885     },
3886     
3887     
3888     initEvents : function()
3889     {
3890         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3891         
3892         if (this.autohide) {
3893             
3894             var prevScroll = 0;
3895             var ft = this.el;
3896             
3897             Roo.get(document).on('scroll',function(e) {
3898                 var ns = Roo.get(document).getScroll().top;
3899                 var os = prevScroll;
3900                 prevScroll = ns;
3901                 
3902                 if(ns > os){
3903                     ft.removeClass('slideDown');
3904                     ft.addClass('slideUp');
3905                     return;
3906                 }
3907                 ft.removeClass('slideUp');
3908                 ft.addClass('slideDown');
3909                  
3910               
3911           },this);
3912         }
3913     }    
3914     
3915 });
3916
3917
3918
3919  
3920
3921  /*
3922  * - LGPL
3923  *
3924  * navbar
3925  * 
3926  */
3927
3928 /**
3929  * @class Roo.bootstrap.NavSidebar
3930  * @extends Roo.bootstrap.Navbar
3931  * Bootstrap Sidebar class
3932  * 
3933  * @constructor
3934  * Create a new Sidebar
3935  * @param {Object} config The config object
3936  */
3937
3938
3939 Roo.bootstrap.NavSidebar = function(config){
3940     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3941 };
3942
3943 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3944     
3945     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3946     
3947     getAutoCreate : function(){
3948         
3949         
3950         return  {
3951             tag: 'div',
3952             cls: 'sidebar sidebar-nav'
3953         };
3954     
3955         
3956     }
3957     
3958     
3959     
3960 });
3961
3962
3963
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * nav group
3970  * 
3971  */
3972
3973 /**
3974  * @class Roo.bootstrap.NavGroup
3975  * @extends Roo.bootstrap.Component
3976  * Bootstrap NavGroup class
3977  * @cfg {String} align (left|right)
3978  * @cfg {Boolean} inverse
3979  * @cfg {String} type (nav|pills|tab) default nav
3980  * @cfg {String} navId - reference Id for navbar.
3981
3982  * 
3983  * @constructor
3984  * Create a new nav group
3985  * @param {Object} config The config object
3986  */
3987
3988 Roo.bootstrap.NavGroup = function(config){
3989     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3990     this.navItems = [];
3991    
3992     Roo.bootstrap.NavGroup.register(this);
3993      this.addEvents({
3994         /**
3995              * @event changed
3996              * Fires when the active item changes
3997              * @param {Roo.bootstrap.NavGroup} this
3998              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3999              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4000          */
4001         'changed': true
4002      });
4003     
4004 };
4005
4006 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4007     
4008     align: '',
4009     inverse: false,
4010     form: false,
4011     type: 'nav',
4012     navId : '',
4013     // private
4014     
4015     navItems : false, 
4016     
4017     getAutoCreate : function()
4018     {
4019         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4020         
4021         cfg = {
4022             tag : 'ul',
4023             cls: 'nav' 
4024         };
4025         
4026         if (['tabs','pills'].indexOf(this.type)!==-1) {
4027             cfg.cls += ' nav-' + this.type
4028         } else {
4029             if (this.type!=='nav') {
4030                 Roo.log('nav type must be nav/tabs/pills')
4031             }
4032             cfg.cls += ' navbar-nav'
4033         }
4034         
4035         if (this.parent() && this.parent().sidebar) {
4036             cfg = {
4037                 tag: 'ul',
4038                 cls: 'dashboard-menu sidebar-menu'
4039             };
4040             
4041             return cfg;
4042         }
4043         
4044         if (this.form === true) {
4045             cfg = {
4046                 tag: 'form',
4047                 cls: 'navbar-form'
4048             };
4049             
4050             if (this.align === 'right') {
4051                 cfg.cls += ' navbar-right';
4052             } else {
4053                 cfg.cls += ' navbar-left';
4054             }
4055         }
4056         
4057         if (this.align === 'right') {
4058             cfg.cls += ' navbar-right';
4059         }
4060         
4061         if (this.inverse) {
4062             cfg.cls += ' navbar-inverse';
4063             
4064         }
4065         
4066         
4067         return cfg;
4068     },
4069     /**
4070     * sets the active Navigation item
4071     * @param {Roo.bootstrap.NavItem} the new current navitem
4072     */
4073     setActiveItem : function(item)
4074     {
4075         var prev = false;
4076         Roo.each(this.navItems, function(v){
4077             if (v == item) {
4078                 return ;
4079             }
4080             if (v.isActive()) {
4081                 v.setActive(false, true);
4082                 prev = v;
4083                 
4084             }
4085             
4086         });
4087
4088         item.setActive(true, true);
4089         this.fireEvent('changed', this, item, prev);
4090         
4091         
4092     },
4093     /**
4094     * gets the active Navigation item
4095     * @return {Roo.bootstrap.NavItem} the current navitem
4096     */
4097     getActive : function()
4098     {
4099         
4100         var prev = false;
4101         Roo.each(this.navItems, function(v){
4102             
4103             if (v.isActive()) {
4104                 prev = v;
4105                 
4106             }
4107             
4108         });
4109         return prev;
4110     },
4111     
4112     indexOfNav : function()
4113     {
4114         
4115         var prev = false;
4116         Roo.each(this.navItems, function(v,i){
4117             
4118             if (v.isActive()) {
4119                 prev = i;
4120                 
4121             }
4122             
4123         });
4124         return prev;
4125     },
4126     /**
4127     * adds a Navigation item
4128     * @param {Roo.bootstrap.NavItem} the navitem to add
4129     */
4130     addItem : function(cfg)
4131     {
4132         var cn = new Roo.bootstrap.NavItem(cfg);
4133         this.register(cn);
4134         cn.parentId = this.id;
4135         cn.onRender(this.el, null);
4136         return cn;
4137     },
4138     /**
4139     * register a Navigation item
4140     * @param {Roo.bootstrap.NavItem} the navitem to add
4141     */
4142     register : function(item)
4143     {
4144         this.navItems.push( item);
4145         item.navId = this.navId;
4146     
4147     },
4148     
4149     /**
4150     * clear all the Navigation item
4151     */
4152    
4153     clearAll : function()
4154     {
4155         this.navItems = [];
4156         this.el.dom.innerHTML = '';
4157     },
4158     
4159     getNavItem: function(tabId)
4160     {
4161         var ret = false;
4162         Roo.each(this.navItems, function(e) {
4163             if (e.tabId == tabId) {
4164                ret =  e;
4165                return false;
4166             }
4167             return true;
4168             
4169         });
4170         return ret;
4171     },
4172     
4173     setActiveNext : function()
4174     {
4175         var i = this.indexOfNav(this.getActive());
4176         if (i > this.navItems.length) {
4177             return;
4178         }
4179         this.setActiveItem(this.navItems[i+1]);
4180     },
4181     setActivePrev : function()
4182     {
4183         var i = this.indexOfNav(this.getActive());
4184         if (i  < 1) {
4185             return;
4186         }
4187         this.setActiveItem(this.navItems[i-1]);
4188     },
4189     clearWasActive : function(except) {
4190         Roo.each(this.navItems, function(e) {
4191             if (e.tabId != except.tabId && e.was_active) {
4192                e.was_active = false;
4193                return false;
4194             }
4195             return true;
4196             
4197         });
4198     },
4199     getWasActive : function ()
4200     {
4201         var r = false;
4202         Roo.each(this.navItems, function(e) {
4203             if (e.was_active) {
4204                r = e;
4205                return false;
4206             }
4207             return true;
4208             
4209         });
4210         return r;
4211     }
4212     
4213     
4214 });
4215
4216  
4217 Roo.apply(Roo.bootstrap.NavGroup, {
4218     
4219     groups: {},
4220      /**
4221     * register a Navigation Group
4222     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4223     */
4224     register : function(navgrp)
4225     {
4226         this.groups[navgrp.navId] = navgrp;
4227         
4228     },
4229     /**
4230     * fetch a Navigation Group based on the navigation ID
4231     * @param {string} the navgroup to add
4232     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4233     */
4234     get: function(navId) {
4235         if (typeof(this.groups[navId]) == 'undefined') {
4236             return false;
4237             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4238         }
4239         return this.groups[navId] ;
4240     }
4241     
4242     
4243     
4244 });
4245
4246  /*
4247  * - LGPL
4248  *
4249  * row
4250  * 
4251  */
4252
4253 /**
4254  * @class Roo.bootstrap.NavItem
4255  * @extends Roo.bootstrap.Component
4256  * Bootstrap Navbar.NavItem class
4257  * @cfg {String} href  link to
4258  * @cfg {String} html content of button
4259  * @cfg {String} badge text inside badge
4260  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4261  * @cfg {String} glyphicon name of glyphicon
4262  * @cfg {String} icon name of font awesome icon
4263  * @cfg {Boolean} active Is item active
4264  * @cfg {Boolean} disabled Is item disabled
4265  
4266  * @cfg {Boolean} preventDefault (true | false) default false
4267  * @cfg {String} tabId the tab that this item activates.
4268  * @cfg {String} tagtype (a|span) render as a href or span?
4269  * @cfg {Boolean} animateRef (true|false) link to element default false  
4270   
4271  * @constructor
4272  * Create a new Navbar Item
4273  * @param {Object} config The config object
4274  */
4275 Roo.bootstrap.NavItem = function(config){
4276     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4277     this.addEvents({
4278         // raw events
4279         /**
4280          * @event click
4281          * The raw click event for the entire grid.
4282          * @param {Roo.EventObject} e
4283          */
4284         "click" : true,
4285          /**
4286             * @event changed
4287             * Fires when the active item active state changes
4288             * @param {Roo.bootstrap.NavItem} this
4289             * @param {boolean} state the new state
4290              
4291          */
4292         'changed': true,
4293         /**
4294             * @event scrollto
4295             * Fires when scroll to element
4296             * @param {Roo.bootstrap.NavItem} this
4297             * @param {Object} options
4298             * @param {Roo.EventObject} e
4299              
4300          */
4301         'scrollto': true
4302     });
4303    
4304 };
4305
4306 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4307     
4308     href: false,
4309     html: '',
4310     badge: '',
4311     icon: false,
4312     glyphicon: false,
4313     active: false,
4314     preventDefault : false,
4315     tabId : false,
4316     tagtype : 'a',
4317     disabled : false,
4318     animateRef : false,
4319     was_active : false,
4320     
4321     getAutoCreate : function(){
4322          
4323         var cfg = {
4324             tag: 'li',
4325             cls: 'nav-item'
4326             
4327         };
4328         
4329         if (this.active) {
4330             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4331         }
4332         if (this.disabled) {
4333             cfg.cls += ' disabled';
4334         }
4335         
4336         if (this.href || this.html || this.glyphicon || this.icon) {
4337             cfg.cn = [
4338                 {
4339                     tag: this.tagtype,
4340                     href : this.href || "#",
4341                     html: this.html || ''
4342                 }
4343             ];
4344             
4345             if (this.icon) {
4346                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4347             }
4348
4349             if(this.glyphicon) {
4350                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4351             }
4352             
4353             if (this.menu) {
4354                 
4355                 cfg.cn[0].html += " <span class='caret'></span>";
4356              
4357             }
4358             
4359             if (this.badge !== '') {
4360                  
4361                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4362             }
4363         }
4364         
4365         
4366         
4367         return cfg;
4368     },
4369     initEvents: function() 
4370     {
4371         if (typeof (this.menu) != 'undefined') {
4372             this.menu.parentType = this.xtype;
4373             this.menu.triggerEl = this.el;
4374             this.menu = this.addxtype(Roo.apply({}, this.menu));
4375         }
4376         
4377         this.el.select('a',true).on('click', this.onClick, this);
4378         
4379         if(this.tagtype == 'span'){
4380             this.el.select('span',true).on('click', this.onClick, this);
4381         }
4382        
4383         // at this point parent should be available..
4384         this.parent().register(this);
4385     },
4386     
4387     onClick : function(e)
4388     {
4389         if (e.getTarget('.dropdown-menu-item')) {
4390             // did you click on a menu itemm.... - then don't trigger onclick..
4391             return;
4392         }
4393         
4394         if(
4395                 this.preventDefault || 
4396                 this.href == '#' 
4397         ){
4398             Roo.log("NavItem - prevent Default?");
4399             e.preventDefault();
4400         }
4401         
4402         if (this.disabled) {
4403             return;
4404         }
4405         
4406         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4407         if (tg && tg.transition) {
4408             Roo.log("waiting for the transitionend");
4409             return;
4410         }
4411         
4412         
4413         
4414         //Roo.log("fire event clicked");
4415         if(this.fireEvent('click', this, e) === false){
4416             return;
4417         };
4418         
4419         if(this.tagtype == 'span'){
4420             return;
4421         }
4422         
4423         //Roo.log(this.href);
4424         var ael = this.el.select('a',true).first();
4425         //Roo.log(ael);
4426         
4427         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4428             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4429             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4430                 return; // ignore... - it's a 'hash' to another page.
4431             }
4432             Roo.log("NavItem - prevent Default?");
4433             e.preventDefault();
4434             this.scrollToElement(e);
4435         }
4436         
4437         
4438         var p =  this.parent();
4439    
4440         if (['tabs','pills'].indexOf(p.type)!==-1) {
4441             if (typeof(p.setActiveItem) !== 'undefined') {
4442                 p.setActiveItem(this);
4443             }
4444         }
4445         
4446         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4447         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4448             // remove the collapsed menu expand...
4449             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4450         }
4451     },
4452     
4453     isActive: function () {
4454         return this.active
4455     },
4456     setActive : function(state, fire, is_was_active)
4457     {
4458         if (this.active && !state && this.navId) {
4459             this.was_active = true;
4460             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4461             if (nv) {
4462                 nv.clearWasActive(this);
4463             }
4464             
4465         }
4466         this.active = state;
4467         
4468         if (!state ) {
4469             this.el.removeClass('active');
4470         } else if (!this.el.hasClass('active')) {
4471             this.el.addClass('active');
4472         }
4473         if (fire) {
4474             this.fireEvent('changed', this, state);
4475         }
4476         
4477         // show a panel if it's registered and related..
4478         
4479         if (!this.navId || !this.tabId || !state || is_was_active) {
4480             return;
4481         }
4482         
4483         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4484         if (!tg) {
4485             return;
4486         }
4487         var pan = tg.getPanelByName(this.tabId);
4488         if (!pan) {
4489             return;
4490         }
4491         // if we can not flip to new panel - go back to old nav highlight..
4492         if (false == tg.showPanel(pan)) {
4493             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4494             if (nv) {
4495                 var onav = nv.getWasActive();
4496                 if (onav) {
4497                     onav.setActive(true, false, true);
4498                 }
4499             }
4500             
4501         }
4502         
4503         
4504         
4505     },
4506      // this should not be here...
4507     setDisabled : function(state)
4508     {
4509         this.disabled = state;
4510         if (!state ) {
4511             this.el.removeClass('disabled');
4512         } else if (!this.el.hasClass('disabled')) {
4513             this.el.addClass('disabled');
4514         }
4515         
4516     },
4517     
4518     /**
4519      * Fetch the element to display the tooltip on.
4520      * @return {Roo.Element} defaults to this.el
4521      */
4522     tooltipEl : function()
4523     {
4524         return this.el.select('' + this.tagtype + '', true).first();
4525     },
4526     
4527     scrollToElement : function(e)
4528     {
4529         var c = document.body;
4530         
4531         /*
4532          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4533          */
4534         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4535             c = document.documentElement;
4536         }
4537         
4538         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4539         
4540         if(!target){
4541             return;
4542         }
4543
4544         var o = target.calcOffsetsTo(c);
4545         
4546         var options = {
4547             target : target,
4548             value : o[1]
4549         };
4550         
4551         this.fireEvent('scrollto', this, options, e);
4552         
4553         Roo.get(c).scrollTo('top', options.value, true);
4554         
4555         return;
4556     }
4557 });
4558  
4559
4560  /*
4561  * - LGPL
4562  *
4563  * sidebar item
4564  *
4565  *  li
4566  *    <span> icon </span>
4567  *    <span> text </span>
4568  *    <span>badge </span>
4569  */
4570
4571 /**
4572  * @class Roo.bootstrap.NavSidebarItem
4573  * @extends Roo.bootstrap.NavItem
4574  * Bootstrap Navbar.NavSidebarItem class
4575  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4576  * {Boolean} open is the menu open
4577  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4578  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4579  * {String} buttonSize (sm|md|lg)the extra classes for the button
4580  * {Boolean} showArrow show arrow next to the text (default true)
4581  * @constructor
4582  * Create a new Navbar Button
4583  * @param {Object} config The config object
4584  */
4585 Roo.bootstrap.NavSidebarItem = function(config){
4586     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4587     this.addEvents({
4588         // raw events
4589         /**
4590          * @event click
4591          * The raw click event for the entire grid.
4592          * @param {Roo.EventObject} e
4593          */
4594         "click" : true,
4595          /**
4596             * @event changed
4597             * Fires when the active item active state changes
4598             * @param {Roo.bootstrap.NavSidebarItem} this
4599             * @param {boolean} state the new state
4600              
4601          */
4602         'changed': true
4603     });
4604    
4605 };
4606
4607 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4608     
4609     badgeWeight : 'default',
4610     
4611     open: false,
4612     
4613     buttonView : false,
4614     
4615     buttonWeight : 'default',
4616     
4617     buttonSize : 'md',
4618     
4619     showArrow : true,
4620     
4621     getAutoCreate : function(){
4622         
4623         
4624         var a = {
4625                 tag: 'a',
4626                 href : this.href || '#',
4627                 cls: '',
4628                 html : '',
4629                 cn : []
4630         };
4631         
4632         if(this.buttonView){
4633             a = {
4634                 tag: 'button',
4635                 href : this.href || '#',
4636                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4637                 html : this.html,
4638                 cn : []
4639             };
4640         }
4641         
4642         var cfg = {
4643             tag: 'li',
4644             cls: '',
4645             cn: [ a ]
4646         };
4647         
4648         if (this.active) {
4649             cfg.cls += ' active';
4650         }
4651         
4652         if (this.disabled) {
4653             cfg.cls += ' disabled';
4654         }
4655         if (this.open) {
4656             cfg.cls += ' open x-open';
4657         }
4658         // left icon..
4659         if (this.glyphicon || this.icon) {
4660             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4661             a.cn.push({ tag : 'i', cls : c }) ;
4662         }
4663         
4664         if(!this.buttonView){
4665             var span = {
4666                 tag: 'span',
4667                 html : this.html || ''
4668             };
4669
4670             a.cn.push(span);
4671             
4672         }
4673         
4674         if (this.badge !== '') {
4675             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4676         }
4677         
4678         if (this.menu) {
4679             
4680             if(this.showArrow){
4681                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4682             }
4683             
4684             a.cls += ' dropdown-toggle treeview' ;
4685         }
4686         
4687         return cfg;
4688     },
4689     
4690     initEvents : function()
4691     { 
4692         if (typeof (this.menu) != 'undefined') {
4693             this.menu.parentType = this.xtype;
4694             this.menu.triggerEl = this.el;
4695             this.menu = this.addxtype(Roo.apply({}, this.menu));
4696         }
4697         
4698         this.el.on('click', this.onClick, this);
4699         
4700         if(this.badge !== ''){
4701             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4702         }
4703         
4704     },
4705     
4706     onClick : function(e)
4707     {
4708         if(this.disabled){
4709             e.preventDefault();
4710             return;
4711         }
4712         
4713         if(this.preventDefault){
4714             e.preventDefault();
4715         }
4716         
4717         this.fireEvent('click', this);
4718     },
4719     
4720     disable : function()
4721     {
4722         this.setDisabled(true);
4723     },
4724     
4725     enable : function()
4726     {
4727         this.setDisabled(false);
4728     },
4729     
4730     setDisabled : function(state)
4731     {
4732         if(this.disabled == state){
4733             return;
4734         }
4735         
4736         this.disabled = state;
4737         
4738         if (state) {
4739             this.el.addClass('disabled');
4740             return;
4741         }
4742         
4743         this.el.removeClass('disabled');
4744         
4745         return;
4746     },
4747     
4748     setActive : function(state)
4749     {
4750         if(this.active == state){
4751             return;
4752         }
4753         
4754         this.active = state;
4755         
4756         if (state) {
4757             this.el.addClass('active');
4758             return;
4759         }
4760         
4761         this.el.removeClass('active');
4762         
4763         return;
4764     },
4765     
4766     isActive: function () 
4767     {
4768         return this.active;
4769     },
4770     
4771     setBadge : function(str)
4772     {
4773         if(!this.badgeEl){
4774             return;
4775         }
4776         
4777         this.badgeEl.dom.innerHTML = str;
4778     }
4779     
4780    
4781      
4782  
4783 });
4784  
4785
4786  /*
4787  * - LGPL
4788  *
4789  * row
4790  * 
4791  */
4792
4793 /**
4794  * @class Roo.bootstrap.Row
4795  * @extends Roo.bootstrap.Component
4796  * Bootstrap Row class (contains columns...)
4797  * 
4798  * @constructor
4799  * Create a new Row
4800  * @param {Object} config The config object
4801  */
4802
4803 Roo.bootstrap.Row = function(config){
4804     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4805 };
4806
4807 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4808     
4809     getAutoCreate : function(){
4810        return {
4811             cls: 'row clearfix'
4812        };
4813     }
4814     
4815     
4816 });
4817
4818  
4819
4820  /*
4821  * - LGPL
4822  *
4823  * element
4824  * 
4825  */
4826
4827 /**
4828  * @class Roo.bootstrap.Element
4829  * @extends Roo.bootstrap.Component
4830  * Bootstrap Element class
4831  * @cfg {String} html contents of the element
4832  * @cfg {String} tag tag of the element
4833  * @cfg {String} cls class of the element
4834  * @cfg {Boolean} preventDefault (true|false) default false
4835  * @cfg {Boolean} clickable (true|false) default false
4836  * 
4837  * @constructor
4838  * Create a new Element
4839  * @param {Object} config The config object
4840  */
4841
4842 Roo.bootstrap.Element = function(config){
4843     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4844     
4845     this.addEvents({
4846         // raw events
4847         /**
4848          * @event click
4849          * When a element is chick
4850          * @param {Roo.bootstrap.Element} this
4851          * @param {Roo.EventObject} e
4852          */
4853         "click" : true
4854     });
4855 };
4856
4857 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4858     
4859     tag: 'div',
4860     cls: '',
4861     html: '',
4862     preventDefault: false, 
4863     clickable: false,
4864     
4865     getAutoCreate : function(){
4866         
4867         var cfg = {
4868             tag: this.tag,
4869             cls: this.cls,
4870             html: this.html
4871         };
4872         
4873         return cfg;
4874     },
4875     
4876     initEvents: function() 
4877     {
4878         Roo.bootstrap.Element.superclass.initEvents.call(this);
4879         
4880         if(this.clickable){
4881             this.el.on('click', this.onClick, this);
4882         }
4883         
4884     },
4885     
4886     onClick : function(e)
4887     {
4888         if(this.preventDefault){
4889             e.preventDefault();
4890         }
4891         
4892         this.fireEvent('click', this, e);
4893     },
4894     
4895     getValue : function()
4896     {
4897         return this.el.dom.innerHTML;
4898     },
4899     
4900     setValue : function(value)
4901     {
4902         this.el.dom.innerHTML = value;
4903     }
4904    
4905 });
4906
4907  
4908
4909  /*
4910  * - LGPL
4911  *
4912  * pagination
4913  * 
4914  */
4915
4916 /**
4917  * @class Roo.bootstrap.Pagination
4918  * @extends Roo.bootstrap.Component
4919  * Bootstrap Pagination class
4920  * @cfg {String} size xs | sm | md | lg
4921  * @cfg {Boolean} inverse false | true
4922  * 
4923  * @constructor
4924  * Create a new Pagination
4925  * @param {Object} config The config object
4926  */
4927
4928 Roo.bootstrap.Pagination = function(config){
4929     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4930 };
4931
4932 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4933     
4934     cls: false,
4935     size: false,
4936     inverse: false,
4937     
4938     getAutoCreate : function(){
4939         var cfg = {
4940             tag: 'ul',
4941                 cls: 'pagination'
4942         };
4943         if (this.inverse) {
4944             cfg.cls += ' inverse';
4945         }
4946         if (this.html) {
4947             cfg.html=this.html;
4948         }
4949         if (this.cls) {
4950             cfg.cls += " " + this.cls;
4951         }
4952         return cfg;
4953     }
4954    
4955 });
4956
4957  
4958
4959  /*
4960  * - LGPL
4961  *
4962  * Pagination item
4963  * 
4964  */
4965
4966
4967 /**
4968  * @class Roo.bootstrap.PaginationItem
4969  * @extends Roo.bootstrap.Component
4970  * Bootstrap PaginationItem class
4971  * @cfg {String} html text
4972  * @cfg {String} href the link
4973  * @cfg {Boolean} preventDefault (true | false) default true
4974  * @cfg {Boolean} active (true | false) default false
4975  * @cfg {Boolean} disabled default false
4976  * 
4977  * 
4978  * @constructor
4979  * Create a new PaginationItem
4980  * @param {Object} config The config object
4981  */
4982
4983
4984 Roo.bootstrap.PaginationItem = function(config){
4985     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4986     this.addEvents({
4987         // raw events
4988         /**
4989          * @event click
4990          * The raw click event for the entire grid.
4991          * @param {Roo.EventObject} e
4992          */
4993         "click" : true
4994     });
4995 };
4996
4997 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4998     
4999     href : false,
5000     html : false,
5001     preventDefault: true,
5002     active : false,
5003     cls : false,
5004     disabled: false,
5005     
5006     getAutoCreate : function(){
5007         var cfg= {
5008             tag: 'li',
5009             cn: [
5010                 {
5011                     tag : 'a',
5012                     href : this.href ? this.href : '#',
5013                     html : this.html ? this.html : ''
5014                 }
5015             ]
5016         };
5017         
5018         if(this.cls){
5019             cfg.cls = this.cls;
5020         }
5021         
5022         if(this.disabled){
5023             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5024         }
5025         
5026         if(this.active){
5027             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5028         }
5029         
5030         return cfg;
5031     },
5032     
5033     initEvents: function() {
5034         
5035         this.el.on('click', this.onClick, this);
5036         
5037     },
5038     onClick : function(e)
5039     {
5040         Roo.log('PaginationItem on click ');
5041         if(this.preventDefault){
5042             e.preventDefault();
5043         }
5044         
5045         if(this.disabled){
5046             return;
5047         }
5048         
5049         this.fireEvent('click', this, e);
5050     }
5051    
5052 });
5053
5054  
5055
5056  /*
5057  * - LGPL
5058  *
5059  * slider
5060  * 
5061  */
5062
5063
5064 /**
5065  * @class Roo.bootstrap.Slider
5066  * @extends Roo.bootstrap.Component
5067  * Bootstrap Slider class
5068  *    
5069  * @constructor
5070  * Create a new Slider
5071  * @param {Object} config The config object
5072  */
5073
5074 Roo.bootstrap.Slider = function(config){
5075     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5076 };
5077
5078 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5079     
5080     getAutoCreate : function(){
5081         
5082         var cfg = {
5083             tag: 'div',
5084             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5085             cn: [
5086                 {
5087                     tag: 'a',
5088                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5089                 }
5090             ]
5091         };
5092         
5093         return cfg;
5094     }
5095    
5096 });
5097
5098  /*
5099  * Based on:
5100  * Ext JS Library 1.1.1
5101  * Copyright(c) 2006-2007, Ext JS, LLC.
5102  *
5103  * Originally Released Under LGPL - original licence link has changed is not relivant.
5104  *
5105  * Fork - LGPL
5106  * <script type="text/javascript">
5107  */
5108  
5109
5110 /**
5111  * @class Roo.grid.ColumnModel
5112  * @extends Roo.util.Observable
5113  * This is the default implementation of a ColumnModel used by the Grid. It defines
5114  * the columns in the grid.
5115  * <br>Usage:<br>
5116  <pre><code>
5117  var colModel = new Roo.grid.ColumnModel([
5118         {header: "Ticker", width: 60, sortable: true, locked: true},
5119         {header: "Company Name", width: 150, sortable: true},
5120         {header: "Market Cap.", width: 100, sortable: true},
5121         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5122         {header: "Employees", width: 100, sortable: true, resizable: false}
5123  ]);
5124  </code></pre>
5125  * <p>
5126  
5127  * The config options listed for this class are options which may appear in each
5128  * individual column definition.
5129  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5130  * @constructor
5131  * @param {Object} config An Array of column config objects. See this class's
5132  * config objects for details.
5133 */
5134 Roo.grid.ColumnModel = function(config){
5135         /**
5136      * The config passed into the constructor
5137      */
5138     this.config = config;
5139     this.lookup = {};
5140
5141     // if no id, create one
5142     // if the column does not have a dataIndex mapping,
5143     // map it to the order it is in the config
5144     for(var i = 0, len = config.length; i < len; i++){
5145         var c = config[i];
5146         if(typeof c.dataIndex == "undefined"){
5147             c.dataIndex = i;
5148         }
5149         if(typeof c.renderer == "string"){
5150             c.renderer = Roo.util.Format[c.renderer];
5151         }
5152         if(typeof c.id == "undefined"){
5153             c.id = Roo.id();
5154         }
5155         if(c.editor && c.editor.xtype){
5156             c.editor  = Roo.factory(c.editor, Roo.grid);
5157         }
5158         if(c.editor && c.editor.isFormField){
5159             c.editor = new Roo.grid.GridEditor(c.editor);
5160         }
5161         this.lookup[c.id] = c;
5162     }
5163
5164     /**
5165      * The width of columns which have no width specified (defaults to 100)
5166      * @type Number
5167      */
5168     this.defaultWidth = 100;
5169
5170     /**
5171      * Default sortable of columns which have no sortable specified (defaults to false)
5172      * @type Boolean
5173      */
5174     this.defaultSortable = false;
5175
5176     this.addEvents({
5177         /**
5178              * @event widthchange
5179              * Fires when the width of a column changes.
5180              * @param {ColumnModel} this
5181              * @param {Number} columnIndex The column index
5182              * @param {Number} newWidth The new width
5183              */
5184             "widthchange": true,
5185         /**
5186              * @event headerchange
5187              * Fires when the text of a header changes.
5188              * @param {ColumnModel} this
5189              * @param {Number} columnIndex The column index
5190              * @param {Number} newText The new header text
5191              */
5192             "headerchange": true,
5193         /**
5194              * @event hiddenchange
5195              * Fires when a column is hidden or "unhidden".
5196              * @param {ColumnModel} this
5197              * @param {Number} columnIndex The column index
5198              * @param {Boolean} hidden true if hidden, false otherwise
5199              */
5200             "hiddenchange": true,
5201             /**
5202          * @event columnmoved
5203          * Fires when a column is moved.
5204          * @param {ColumnModel} this
5205          * @param {Number} oldIndex
5206          * @param {Number} newIndex
5207          */
5208         "columnmoved" : true,
5209         /**
5210          * @event columlockchange
5211          * Fires when a column's locked state is changed
5212          * @param {ColumnModel} this
5213          * @param {Number} colIndex
5214          * @param {Boolean} locked true if locked
5215          */
5216         "columnlockchange" : true
5217     });
5218     Roo.grid.ColumnModel.superclass.constructor.call(this);
5219 };
5220 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5221     /**
5222      * @cfg {String} header The header text to display in the Grid view.
5223      */
5224     /**
5225      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5226      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5227      * specified, the column's index is used as an index into the Record's data Array.
5228      */
5229     /**
5230      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5231      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5232      */
5233     /**
5234      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5235      * Defaults to the value of the {@link #defaultSortable} property.
5236      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5237      */
5238     /**
5239      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5240      */
5241     /**
5242      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5243      */
5244     /**
5245      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5246      */
5247     /**
5248      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5249      */
5250     /**
5251      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5252      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5253      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5254      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5255      */
5256        /**
5257      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5258      */
5259     /**
5260      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5261      */
5262     /**
5263      * @cfg {String} cursor (Optional)
5264      */
5265     /**
5266      * @cfg {String} tooltip (Optional)
5267      */
5268     /**
5269      * @cfg {Number} xs (Optional)
5270      */
5271     /**
5272      * @cfg {Number} sm (Optional)
5273      */
5274     /**
5275      * @cfg {Number} md (Optional)
5276      */
5277     /**
5278      * @cfg {Number} lg (Optional)
5279      */
5280     /**
5281      * Returns the id of the column at the specified index.
5282      * @param {Number} index The column index
5283      * @return {String} the id
5284      */
5285     getColumnId : function(index){
5286         return this.config[index].id;
5287     },
5288
5289     /**
5290      * Returns the column for a specified id.
5291      * @param {String} id The column id
5292      * @return {Object} the column
5293      */
5294     getColumnById : function(id){
5295         return this.lookup[id];
5296     },
5297
5298     
5299     /**
5300      * Returns the column for a specified dataIndex.
5301      * @param {String} dataIndex The column dataIndex
5302      * @return {Object|Boolean} the column or false if not found
5303      */
5304     getColumnByDataIndex: function(dataIndex){
5305         var index = this.findColumnIndex(dataIndex);
5306         return index > -1 ? this.config[index] : false;
5307     },
5308     
5309     /**
5310      * Returns the index for a specified column id.
5311      * @param {String} id The column id
5312      * @return {Number} the index, or -1 if not found
5313      */
5314     getIndexById : function(id){
5315         for(var i = 0, len = this.config.length; i < len; i++){
5316             if(this.config[i].id == id){
5317                 return i;
5318             }
5319         }
5320         return -1;
5321     },
5322     
5323     /**
5324      * Returns the index for a specified column dataIndex.
5325      * @param {String} dataIndex The column dataIndex
5326      * @return {Number} the index, or -1 if not found
5327      */
5328     
5329     findColumnIndex : function(dataIndex){
5330         for(var i = 0, len = this.config.length; i < len; i++){
5331             if(this.config[i].dataIndex == dataIndex){
5332                 return i;
5333             }
5334         }
5335         return -1;
5336     },
5337     
5338     
5339     moveColumn : function(oldIndex, newIndex){
5340         var c = this.config[oldIndex];
5341         this.config.splice(oldIndex, 1);
5342         this.config.splice(newIndex, 0, c);
5343         this.dataMap = null;
5344         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5345     },
5346
5347     isLocked : function(colIndex){
5348         return this.config[colIndex].locked === true;
5349     },
5350
5351     setLocked : function(colIndex, value, suppressEvent){
5352         if(this.isLocked(colIndex) == value){
5353             return;
5354         }
5355         this.config[colIndex].locked = value;
5356         if(!suppressEvent){
5357             this.fireEvent("columnlockchange", this, colIndex, value);
5358         }
5359     },
5360
5361     getTotalLockedWidth : function(){
5362         var totalWidth = 0;
5363         for(var i = 0; i < this.config.length; i++){
5364             if(this.isLocked(i) && !this.isHidden(i)){
5365                 this.totalWidth += this.getColumnWidth(i);
5366             }
5367         }
5368         return totalWidth;
5369     },
5370
5371     getLockedCount : function(){
5372         for(var i = 0, len = this.config.length; i < len; i++){
5373             if(!this.isLocked(i)){
5374                 return i;
5375             }
5376         }
5377         
5378         return this.config.length;
5379     },
5380
5381     /**
5382      * Returns the number of columns.
5383      * @return {Number}
5384      */
5385     getColumnCount : function(visibleOnly){
5386         if(visibleOnly === true){
5387             var c = 0;
5388             for(var i = 0, len = this.config.length; i < len; i++){
5389                 if(!this.isHidden(i)){
5390                     c++;
5391                 }
5392             }
5393             return c;
5394         }
5395         return this.config.length;
5396     },
5397
5398     /**
5399      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5400      * @param {Function} fn
5401      * @param {Object} scope (optional)
5402      * @return {Array} result
5403      */
5404     getColumnsBy : function(fn, scope){
5405         var r = [];
5406         for(var i = 0, len = this.config.length; i < len; i++){
5407             var c = this.config[i];
5408             if(fn.call(scope||this, c, i) === true){
5409                 r[r.length] = c;
5410             }
5411         }
5412         return r;
5413     },
5414
5415     /**
5416      * Returns true if the specified column is sortable.
5417      * @param {Number} col The column index
5418      * @return {Boolean}
5419      */
5420     isSortable : function(col){
5421         if(typeof this.config[col].sortable == "undefined"){
5422             return this.defaultSortable;
5423         }
5424         return this.config[col].sortable;
5425     },
5426
5427     /**
5428      * Returns the rendering (formatting) function defined for the column.
5429      * @param {Number} col The column index.
5430      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5431      */
5432     getRenderer : function(col){
5433         if(!this.config[col].renderer){
5434             return Roo.grid.ColumnModel.defaultRenderer;
5435         }
5436         return this.config[col].renderer;
5437     },
5438
5439     /**
5440      * Sets the rendering (formatting) function for a column.
5441      * @param {Number} col The column index
5442      * @param {Function} fn The function to use to process the cell's raw data
5443      * to return HTML markup for the grid view. The render function is called with
5444      * the following parameters:<ul>
5445      * <li>Data value.</li>
5446      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5447      * <li>css A CSS style string to apply to the table cell.</li>
5448      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5449      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5450      * <li>Row index</li>
5451      * <li>Column index</li>
5452      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5453      */
5454     setRenderer : function(col, fn){
5455         this.config[col].renderer = fn;
5456     },
5457
5458     /**
5459      * Returns the width for the specified column.
5460      * @param {Number} col The column index
5461      * @return {Number}
5462      */
5463     getColumnWidth : function(col){
5464         return this.config[col].width * 1 || this.defaultWidth;
5465     },
5466
5467     /**
5468      * Sets the width for a column.
5469      * @param {Number} col The column index
5470      * @param {Number} width The new width
5471      */
5472     setColumnWidth : function(col, width, suppressEvent){
5473         this.config[col].width = width;
5474         this.totalWidth = null;
5475         if(!suppressEvent){
5476              this.fireEvent("widthchange", this, col, width);
5477         }
5478     },
5479
5480     /**
5481      * Returns the total width of all columns.
5482      * @param {Boolean} includeHidden True to include hidden column widths
5483      * @return {Number}
5484      */
5485     getTotalWidth : function(includeHidden){
5486         if(!this.totalWidth){
5487             this.totalWidth = 0;
5488             for(var i = 0, len = this.config.length; i < len; i++){
5489                 if(includeHidden || !this.isHidden(i)){
5490                     this.totalWidth += this.getColumnWidth(i);
5491                 }
5492             }
5493         }
5494         return this.totalWidth;
5495     },
5496
5497     /**
5498      * Returns the header for the specified column.
5499      * @param {Number} col The column index
5500      * @return {String}
5501      */
5502     getColumnHeader : function(col){
5503         return this.config[col].header;
5504     },
5505
5506     /**
5507      * Sets the header for a column.
5508      * @param {Number} col The column index
5509      * @param {String} header The new header
5510      */
5511     setColumnHeader : function(col, header){
5512         this.config[col].header = header;
5513         this.fireEvent("headerchange", this, col, header);
5514     },
5515
5516     /**
5517      * Returns the tooltip for the specified column.
5518      * @param {Number} col The column index
5519      * @return {String}
5520      */
5521     getColumnTooltip : function(col){
5522             return this.config[col].tooltip;
5523     },
5524     /**
5525      * Sets the tooltip for a column.
5526      * @param {Number} col The column index
5527      * @param {String} tooltip The new tooltip
5528      */
5529     setColumnTooltip : function(col, tooltip){
5530             this.config[col].tooltip = tooltip;
5531     },
5532
5533     /**
5534      * Returns the dataIndex for the specified column.
5535      * @param {Number} col The column index
5536      * @return {Number}
5537      */
5538     getDataIndex : function(col){
5539         return this.config[col].dataIndex;
5540     },
5541
5542     /**
5543      * Sets the dataIndex for a column.
5544      * @param {Number} col The column index
5545      * @param {Number} dataIndex The new dataIndex
5546      */
5547     setDataIndex : function(col, dataIndex){
5548         this.config[col].dataIndex = dataIndex;
5549     },
5550
5551     
5552     
5553     /**
5554      * Returns true if the cell is editable.
5555      * @param {Number} colIndex The column index
5556      * @param {Number} rowIndex The row index - this is nto actually used..?
5557      * @return {Boolean}
5558      */
5559     isCellEditable : function(colIndex, rowIndex){
5560         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5561     },
5562
5563     /**
5564      * Returns the editor defined for the cell/column.
5565      * return false or null to disable editing.
5566      * @param {Number} colIndex The column index
5567      * @param {Number} rowIndex The row index
5568      * @return {Object}
5569      */
5570     getCellEditor : function(colIndex, rowIndex){
5571         return this.config[colIndex].editor;
5572     },
5573
5574     /**
5575      * Sets if a column is editable.
5576      * @param {Number} col The column index
5577      * @param {Boolean} editable True if the column is editable
5578      */
5579     setEditable : function(col, editable){
5580         this.config[col].editable = editable;
5581     },
5582
5583
5584     /**
5585      * Returns true if the column is hidden.
5586      * @param {Number} colIndex The column index
5587      * @return {Boolean}
5588      */
5589     isHidden : function(colIndex){
5590         return this.config[colIndex].hidden;
5591     },
5592
5593
5594     /**
5595      * Returns true if the column width cannot be changed
5596      */
5597     isFixed : function(colIndex){
5598         return this.config[colIndex].fixed;
5599     },
5600
5601     /**
5602      * Returns true if the column can be resized
5603      * @return {Boolean}
5604      */
5605     isResizable : function(colIndex){
5606         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5607     },
5608     /**
5609      * Sets if a column is hidden.
5610      * @param {Number} colIndex The column index
5611      * @param {Boolean} hidden True if the column is hidden
5612      */
5613     setHidden : function(colIndex, hidden){
5614         this.config[colIndex].hidden = hidden;
5615         this.totalWidth = null;
5616         this.fireEvent("hiddenchange", this, colIndex, hidden);
5617     },
5618
5619     /**
5620      * Sets the editor for a column.
5621      * @param {Number} col The column index
5622      * @param {Object} editor The editor object
5623      */
5624     setEditor : function(col, editor){
5625         this.config[col].editor = editor;
5626     }
5627 });
5628
5629 Roo.grid.ColumnModel.defaultRenderer = function(value)
5630 {
5631     if(typeof value == "object") {
5632         return value;
5633     }
5634         if(typeof value == "string" && value.length < 1){
5635             return "&#160;";
5636         }
5637     
5638         return String.format("{0}", value);
5639 };
5640
5641 // Alias for backwards compatibility
5642 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5643 /*
5644  * Based on:
5645  * Ext JS Library 1.1.1
5646  * Copyright(c) 2006-2007, Ext JS, LLC.
5647  *
5648  * Originally Released Under LGPL - original licence link has changed is not relivant.
5649  *
5650  * Fork - LGPL
5651  * <script type="text/javascript">
5652  */
5653  
5654 /**
5655  * @class Roo.LoadMask
5656  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5657  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5658  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5659  * element's UpdateManager load indicator and will be destroyed after the initial load.
5660  * @constructor
5661  * Create a new LoadMask
5662  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5663  * @param {Object} config The config object
5664  */
5665 Roo.LoadMask = function(el, config){
5666     this.el = Roo.get(el);
5667     Roo.apply(this, config);
5668     if(this.store){
5669         this.store.on('beforeload', this.onBeforeLoad, this);
5670         this.store.on('load', this.onLoad, this);
5671         this.store.on('loadexception', this.onLoadException, this);
5672         this.removeMask = false;
5673     }else{
5674         var um = this.el.getUpdateManager();
5675         um.showLoadIndicator = false; // disable the default indicator
5676         um.on('beforeupdate', this.onBeforeLoad, this);
5677         um.on('update', this.onLoad, this);
5678         um.on('failure', this.onLoad, this);
5679         this.removeMask = true;
5680     }
5681 };
5682
5683 Roo.LoadMask.prototype = {
5684     /**
5685      * @cfg {Boolean} removeMask
5686      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5687      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5688      */
5689     /**
5690      * @cfg {String} msg
5691      * The text to display in a centered loading message box (defaults to 'Loading...')
5692      */
5693     msg : 'Loading...',
5694     /**
5695      * @cfg {String} msgCls
5696      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5697      */
5698     msgCls : 'x-mask-loading',
5699
5700     /**
5701      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5702      * @type Boolean
5703      */
5704     disabled: false,
5705
5706     /**
5707      * Disables the mask to prevent it from being displayed
5708      */
5709     disable : function(){
5710        this.disabled = true;
5711     },
5712
5713     /**
5714      * Enables the mask so that it can be displayed
5715      */
5716     enable : function(){
5717         this.disabled = false;
5718     },
5719     
5720     onLoadException : function()
5721     {
5722         Roo.log(arguments);
5723         
5724         if (typeof(arguments[3]) != 'undefined') {
5725             Roo.MessageBox.alert("Error loading",arguments[3]);
5726         } 
5727         /*
5728         try {
5729             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5730                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5731             }   
5732         } catch(e) {
5733             
5734         }
5735         */
5736     
5737         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5738     },
5739     // private
5740     onLoad : function()
5741     {
5742         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5743     },
5744
5745     // private
5746     onBeforeLoad : function(){
5747         if(!this.disabled){
5748             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5749         }
5750     },
5751
5752     // private
5753     destroy : function(){
5754         if(this.store){
5755             this.store.un('beforeload', this.onBeforeLoad, this);
5756             this.store.un('load', this.onLoad, this);
5757             this.store.un('loadexception', this.onLoadException, this);
5758         }else{
5759             var um = this.el.getUpdateManager();
5760             um.un('beforeupdate', this.onBeforeLoad, this);
5761             um.un('update', this.onLoad, this);
5762             um.un('failure', this.onLoad, this);
5763         }
5764     }
5765 };/*
5766  * - LGPL
5767  *
5768  * table
5769  * 
5770  */
5771
5772 /**
5773  * @class Roo.bootstrap.Table
5774  * @extends Roo.bootstrap.Component
5775  * Bootstrap Table class
5776  * @cfg {String} cls table class
5777  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5778  * @cfg {String} bgcolor Specifies the background color for a table
5779  * @cfg {Number} border Specifies whether the table cells should have borders or not
5780  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5781  * @cfg {Number} cellspacing Specifies the space between cells
5782  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5783  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5784  * @cfg {String} sortable Specifies that the table should be sortable
5785  * @cfg {String} summary Specifies a summary of the content of a table
5786  * @cfg {Number} width Specifies the width of a table
5787  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5788  * 
5789  * @cfg {boolean} striped Should the rows be alternative striped
5790  * @cfg {boolean} bordered Add borders to the table
5791  * @cfg {boolean} hover Add hover highlighting
5792  * @cfg {boolean} condensed Format condensed
5793  * @cfg {boolean} responsive Format condensed
5794  * @cfg {Boolean} loadMask (true|false) default false
5795  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5796  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5797  * @cfg {Boolean} rowSelection (true|false) default false
5798  * @cfg {Boolean} cellSelection (true|false) default false
5799  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5800  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5801  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5802  
5803  * 
5804  * @constructor
5805  * Create a new Table
5806  * @param {Object} config The config object
5807  */
5808
5809 Roo.bootstrap.Table = function(config){
5810     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5811     
5812   
5813     
5814     // BC...
5815     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5816     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5817     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5818     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5819     
5820     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5821     if (this.sm) {
5822         this.sm.grid = this;
5823         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5824         this.sm = this.selModel;
5825         this.sm.xmodule = this.xmodule || false;
5826     }
5827     
5828     if (this.cm && typeof(this.cm.config) == 'undefined') {
5829         this.colModel = new Roo.grid.ColumnModel(this.cm);
5830         this.cm = this.colModel;
5831         this.cm.xmodule = this.xmodule || false;
5832     }
5833     if (this.store) {
5834         this.store= Roo.factory(this.store, Roo.data);
5835         this.ds = this.store;
5836         this.ds.xmodule = this.xmodule || false;
5837          
5838     }
5839     if (this.footer && this.store) {
5840         this.footer.dataSource = this.ds;
5841         this.footer = Roo.factory(this.footer);
5842     }
5843     
5844     /** @private */
5845     this.addEvents({
5846         /**
5847          * @event cellclick
5848          * Fires when a cell is clicked
5849          * @param {Roo.bootstrap.Table} this
5850          * @param {Roo.Element} el
5851          * @param {Number} rowIndex
5852          * @param {Number} columnIndex
5853          * @param {Roo.EventObject} e
5854          */
5855         "cellclick" : true,
5856         /**
5857          * @event celldblclick
5858          * Fires when a cell is double clicked
5859          * @param {Roo.bootstrap.Table} this
5860          * @param {Roo.Element} el
5861          * @param {Number} rowIndex
5862          * @param {Number} columnIndex
5863          * @param {Roo.EventObject} e
5864          */
5865         "celldblclick" : true,
5866         /**
5867          * @event rowclick
5868          * Fires when a row is clicked
5869          * @param {Roo.bootstrap.Table} this
5870          * @param {Roo.Element} el
5871          * @param {Number} rowIndex
5872          * @param {Roo.EventObject} e
5873          */
5874         "rowclick" : true,
5875         /**
5876          * @event rowdblclick
5877          * Fires when a row is double clicked
5878          * @param {Roo.bootstrap.Table} this
5879          * @param {Roo.Element} el
5880          * @param {Number} rowIndex
5881          * @param {Roo.EventObject} e
5882          */
5883         "rowdblclick" : true,
5884         /**
5885          * @event mouseover
5886          * Fires when a mouseover occur
5887          * @param {Roo.bootstrap.Table} this
5888          * @param {Roo.Element} el
5889          * @param {Number} rowIndex
5890          * @param {Number} columnIndex
5891          * @param {Roo.EventObject} e
5892          */
5893         "mouseover" : true,
5894         /**
5895          * @event mouseout
5896          * Fires when a mouseout occur
5897          * @param {Roo.bootstrap.Table} this
5898          * @param {Roo.Element} el
5899          * @param {Number} rowIndex
5900          * @param {Number} columnIndex
5901          * @param {Roo.EventObject} e
5902          */
5903         "mouseout" : true,
5904         /**
5905          * @event rowclass
5906          * Fires when a row is rendered, so you can change add a style to it.
5907          * @param {Roo.bootstrap.Table} this
5908          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5909          */
5910         'rowclass' : true,
5911           /**
5912          * @event rowsrendered
5913          * Fires when all the  rows have been rendered
5914          * @param {Roo.bootstrap.Table} this
5915          */
5916         'rowsrendered' : true,
5917         /**
5918          * @event contextmenu
5919          * The raw contextmenu event for the entire grid.
5920          * @param {Roo.EventObject} e
5921          */
5922         "contextmenu" : true,
5923         /**
5924          * @event rowcontextmenu
5925          * Fires when a row is right clicked
5926          * @param {Roo.bootstrap.Table} this
5927          * @param {Number} rowIndex
5928          * @param {Roo.EventObject} e
5929          */
5930         "rowcontextmenu" : true,
5931         /**
5932          * @event cellcontextmenu
5933          * Fires when a cell is right clicked
5934          * @param {Roo.bootstrap.Table} this
5935          * @param {Number} rowIndex
5936          * @param {Number} cellIndex
5937          * @param {Roo.EventObject} e
5938          */
5939          "cellcontextmenu" : true,
5940          /**
5941          * @event headercontextmenu
5942          * Fires when a header is right clicked
5943          * @param {Roo.bootstrap.Table} this
5944          * @param {Number} columnIndex
5945          * @param {Roo.EventObject} e
5946          */
5947         "headercontextmenu" : true
5948     });
5949 };
5950
5951 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5952     
5953     cls: false,
5954     align: false,
5955     bgcolor: false,
5956     border: false,
5957     cellpadding: false,
5958     cellspacing: false,
5959     frame: false,
5960     rules: false,
5961     sortable: false,
5962     summary: false,
5963     width: false,
5964     striped : false,
5965     scrollBody : false,
5966     bordered: false,
5967     hover:  false,
5968     condensed : false,
5969     responsive : false,
5970     sm : false,
5971     cm : false,
5972     store : false,
5973     loadMask : false,
5974     footerShow : true,
5975     headerShow : true,
5976   
5977     rowSelection : false,
5978     cellSelection : false,
5979     layout : false,
5980     
5981     // Roo.Element - the tbody
5982     mainBody: false,
5983     // Roo.Element - thead element
5984     mainHead: false,
5985     
5986     container: false, // used by gridpanel...
5987     
5988     lazyLoad : false,
5989     
5990     getAutoCreate : function()
5991     {
5992         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5993         
5994         cfg = {
5995             tag: 'table',
5996             cls : 'table',
5997             cn : []
5998         };
5999         if (this.scrollBody) {
6000             cfg.cls += ' table-body-fixed';
6001         }    
6002         if (this.striped) {
6003             cfg.cls += ' table-striped';
6004         }
6005         
6006         if (this.hover) {
6007             cfg.cls += ' table-hover';
6008         }
6009         if (this.bordered) {
6010             cfg.cls += ' table-bordered';
6011         }
6012         if (this.condensed) {
6013             cfg.cls += ' table-condensed';
6014         }
6015         if (this.responsive) {
6016             cfg.cls += ' table-responsive';
6017         }
6018         
6019         if (this.cls) {
6020             cfg.cls+=  ' ' +this.cls;
6021         }
6022         
6023         // this lot should be simplifed...
6024         
6025         if (this.align) {
6026             cfg.align=this.align;
6027         }
6028         if (this.bgcolor) {
6029             cfg.bgcolor=this.bgcolor;
6030         }
6031         if (this.border) {
6032             cfg.border=this.border;
6033         }
6034         if (this.cellpadding) {
6035             cfg.cellpadding=this.cellpadding;
6036         }
6037         if (this.cellspacing) {
6038             cfg.cellspacing=this.cellspacing;
6039         }
6040         if (this.frame) {
6041             cfg.frame=this.frame;
6042         }
6043         if (this.rules) {
6044             cfg.rules=this.rules;
6045         }
6046         if (this.sortable) {
6047             cfg.sortable=this.sortable;
6048         }
6049         if (this.summary) {
6050             cfg.summary=this.summary;
6051         }
6052         if (this.width) {
6053             cfg.width=this.width;
6054         }
6055         if (this.layout) {
6056             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6057         }
6058         
6059         if(this.store || this.cm){
6060             if(this.headerShow){
6061                 cfg.cn.push(this.renderHeader());
6062             }
6063             
6064             cfg.cn.push(this.renderBody());
6065             
6066             if(this.footerShow){
6067                 cfg.cn.push(this.renderFooter());
6068             }
6069             // where does this come from?
6070             //cfg.cls+=  ' TableGrid';
6071         }
6072         
6073         return { cn : [ cfg ] };
6074     },
6075     
6076     initEvents : function()
6077     {   
6078         if(!this.store || !this.cm){
6079             return;
6080         }
6081         if (this.selModel) {
6082             this.selModel.initEvents();
6083         }
6084         
6085         
6086         //Roo.log('initEvents with ds!!!!');
6087         
6088         this.mainBody = this.el.select('tbody', true).first();
6089         this.mainHead = this.el.select('thead', true).first();
6090         
6091         
6092         
6093         
6094         var _this = this;
6095         
6096         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6097             e.on('click', _this.sort, _this);
6098         });
6099         
6100         this.mainBody.on("click", this.onClick, this);
6101         this.mainBody.on("dblclick", this.onDblClick, this);
6102         
6103         // why is this done????? = it breaks dialogs??
6104         //this.parent().el.setStyle('position', 'relative');
6105         
6106         
6107         if (this.footer) {
6108             this.footer.parentId = this.id;
6109             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6110             
6111             if(this.lazyLoad){
6112                 this.el.select('tfoot tr td').first().addClass('hide');
6113             }
6114         } 
6115         
6116         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6117         
6118         this.store.on('load', this.onLoad, this);
6119         this.store.on('beforeload', this.onBeforeLoad, this);
6120         this.store.on('update', this.onUpdate, this);
6121         this.store.on('add', this.onAdd, this);
6122         this.store.on("clear", this.clear, this);
6123         
6124         this.el.on("contextmenu", this.onContextMenu, this);
6125         
6126         this.mainBody.on('scroll', this.onBodyScroll, this);
6127         
6128         
6129     },
6130     
6131     onContextMenu : function(e, t)
6132     {
6133         this.processEvent("contextmenu", e);
6134     },
6135     
6136     processEvent : function(name, e)
6137     {
6138         if (name != 'touchstart' ) {
6139             this.fireEvent(name, e);    
6140         }
6141         
6142         var t = e.getTarget();
6143         
6144         var cell = Roo.get(t);
6145         
6146         if(!cell){
6147             return;
6148         }
6149         
6150         if(cell.findParent('tfoot', false, true)){
6151             return;
6152         }
6153         
6154         if(cell.findParent('thead', false, true)){
6155             
6156             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6157                 cell = Roo.get(t).findParent('th', false, true);
6158                 if (!cell) {
6159                     Roo.log("failed to find th in thead?");
6160                     Roo.log(e.getTarget());
6161                     return;
6162                 }
6163             }
6164             
6165             var cellIndex = cell.dom.cellIndex;
6166             
6167             var ename = name == 'touchstart' ? 'click' : name;
6168             this.fireEvent("header" + ename, this, cellIndex, e);
6169             
6170             return;
6171         }
6172         
6173         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6174             cell = Roo.get(t).findParent('td', false, true);
6175             if (!cell) {
6176                 Roo.log("failed to find th in tbody?");
6177                 Roo.log(e.getTarget());
6178                 return;
6179             }
6180         }
6181         
6182         var row = cell.findParent('tr', false, true);
6183         var cellIndex = cell.dom.cellIndex;
6184         var rowIndex = row.dom.rowIndex - 1;
6185         
6186         if(row !== false){
6187             
6188             this.fireEvent("row" + name, this, rowIndex, e);
6189             
6190             if(cell !== false){
6191             
6192                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6193             }
6194         }
6195         
6196     },
6197     
6198     onMouseover : function(e, el)
6199     {
6200         var cell = Roo.get(el);
6201         
6202         if(!cell){
6203             return;
6204         }
6205         
6206         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6207             cell = cell.findParent('td', false, true);
6208         }
6209         
6210         var row = cell.findParent('tr', false, true);
6211         var cellIndex = cell.dom.cellIndex;
6212         var rowIndex = row.dom.rowIndex - 1; // start from 0
6213         
6214         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6215         
6216     },
6217     
6218     onMouseout : function(e, el)
6219     {
6220         var cell = Roo.get(el);
6221         
6222         if(!cell){
6223             return;
6224         }
6225         
6226         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6227             cell = cell.findParent('td', false, true);
6228         }
6229         
6230         var row = cell.findParent('tr', false, true);
6231         var cellIndex = cell.dom.cellIndex;
6232         var rowIndex = row.dom.rowIndex - 1; // start from 0
6233         
6234         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6235         
6236     },
6237     
6238     onClick : function(e, el)
6239     {
6240         var cell = Roo.get(el);
6241         
6242         if(!cell || (!this.cellSelection && !this.rowSelection)){
6243             return;
6244         }
6245         
6246         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6247             cell = cell.findParent('td', false, true);
6248         }
6249         
6250         if(!cell || typeof(cell) == 'undefined'){
6251             return;
6252         }
6253         
6254         var row = cell.findParent('tr', false, true);
6255         
6256         if(!row || typeof(row) == 'undefined'){
6257             return;
6258         }
6259         
6260         var cellIndex = cell.dom.cellIndex;
6261         var rowIndex = this.getRowIndex(row);
6262         
6263         // why??? - should these not be based on SelectionModel?
6264         if(this.cellSelection){
6265             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6266         }
6267         
6268         if(this.rowSelection){
6269             this.fireEvent('rowclick', this, row, rowIndex, e);
6270         }
6271         
6272         
6273     },
6274         
6275     onDblClick : function(e,el)
6276     {
6277         var cell = Roo.get(el);
6278         
6279         if(!cell || (!this.cellSelection && !this.rowSelection)){
6280             return;
6281         }
6282         
6283         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6284             cell = cell.findParent('td', false, true);
6285         }
6286         
6287         if(!cell || typeof(cell) == 'undefined'){
6288             return;
6289         }
6290         
6291         var row = cell.findParent('tr', false, true);
6292         
6293         if(!row || typeof(row) == 'undefined'){
6294             return;
6295         }
6296         
6297         var cellIndex = cell.dom.cellIndex;
6298         var rowIndex = this.getRowIndex(row);
6299         
6300         if(this.cellSelection){
6301             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6302         }
6303         
6304         if(this.rowSelection){
6305             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6306         }
6307     },
6308     
6309     sort : function(e,el)
6310     {
6311         var col = Roo.get(el);
6312         
6313         if(!col.hasClass('sortable')){
6314             return;
6315         }
6316         
6317         var sort = col.attr('sort');
6318         var dir = 'ASC';
6319         
6320         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6321             dir = 'DESC';
6322         }
6323         
6324         this.store.sortInfo = {field : sort, direction : dir};
6325         
6326         if (this.footer) {
6327             Roo.log("calling footer first");
6328             this.footer.onClick('first');
6329         } else {
6330         
6331             this.store.load({ params : { start : 0 } });
6332         }
6333     },
6334     
6335     renderHeader : function()
6336     {
6337         var header = {
6338             tag: 'thead',
6339             cn : []
6340         };
6341         
6342         var cm = this.cm;
6343         this.totalWidth = 0;
6344         
6345         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6346             
6347             var config = cm.config[i];
6348             
6349             var c = {
6350                 tag: 'th',
6351                 style : '',
6352                 html: cm.getColumnHeader(i)
6353             };
6354             
6355             var hh = '';
6356             
6357             if(typeof(config.sortable) != 'undefined' && config.sortable){
6358                 c.cls = 'sortable';
6359                 c.html = '<i class="glyphicon"></i>' + c.html;
6360             }
6361             
6362             if(typeof(config.lgHeader) != 'undefined'){
6363                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6364             }
6365             
6366             if(typeof(config.mdHeader) != 'undefined'){
6367                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6368             }
6369             
6370             if(typeof(config.smHeader) != 'undefined'){
6371                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6372             }
6373             
6374             if(typeof(config.xsHeader) != 'undefined'){
6375                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6376             }
6377             
6378             if(hh.length){
6379                 c.html = hh;
6380             }
6381             
6382             if(typeof(config.tooltip) != 'undefined'){
6383                 c.tooltip = config.tooltip;
6384             }
6385             
6386             if(typeof(config.colspan) != 'undefined'){
6387                 c.colspan = config.colspan;
6388             }
6389             
6390             if(typeof(config.hidden) != 'undefined' && config.hidden){
6391                 c.style += ' display:none;';
6392             }
6393             
6394             if(typeof(config.dataIndex) != 'undefined'){
6395                 c.sort = config.dataIndex;
6396             }
6397             
6398            
6399             
6400             if(typeof(config.align) != 'undefined' && config.align.length){
6401                 c.style += ' text-align:' + config.align + ';';
6402             }
6403             
6404             if(typeof(config.width) != 'undefined'){
6405                 c.style += ' width:' + config.width + 'px;';
6406                 this.totalWidth += config.width;
6407             } else {
6408                 this.totalWidth += 100; // assume minimum of 100 per column?
6409             }
6410             
6411             if(typeof(config.cls) != 'undefined'){
6412                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6413             }
6414             
6415             ['xs','sm','md','lg'].map(function(size){
6416                 
6417                 if(typeof(config[size]) == 'undefined'){
6418                     return;
6419                 }
6420                 
6421                 if (!config[size]) { // 0 = hidden
6422                     c.cls += ' hidden-' + size;
6423                     return;
6424                 }
6425                 
6426                 c.cls += ' col-' + size + '-' + config[size];
6427
6428             });
6429             
6430             header.cn.push(c)
6431         }
6432         
6433         return header;
6434     },
6435     
6436     renderBody : function()
6437     {
6438         var body = {
6439             tag: 'tbody',
6440             cn : [
6441                 {
6442                     tag: 'tr',
6443                     cn : [
6444                         {
6445                             tag : 'td',
6446                             colspan :  this.cm.getColumnCount()
6447                         }
6448                     ]
6449                 }
6450             ]
6451         };
6452         
6453         return body;
6454     },
6455     
6456     renderFooter : function()
6457     {
6458         var footer = {
6459             tag: 'tfoot',
6460             cn : [
6461                 {
6462                     tag: 'tr',
6463                     cn : [
6464                         {
6465                             tag : 'td',
6466                             colspan :  this.cm.getColumnCount()
6467                         }
6468                     ]
6469                 }
6470             ]
6471         };
6472         
6473         return footer;
6474     },
6475     
6476     
6477     
6478     onLoad : function()
6479     {
6480 //        Roo.log('ds onload');
6481         this.clear();
6482         
6483         var _this = this;
6484         var cm = this.cm;
6485         var ds = this.store;
6486         
6487         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6488             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6489             if (_this.store.sortInfo) {
6490                     
6491                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6492                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6493                 }
6494                 
6495                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6496                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6497                 }
6498             }
6499         });
6500         
6501         var tbody =  this.mainBody;
6502               
6503         if(ds.getCount() > 0){
6504             ds.data.each(function(d,rowIndex){
6505                 var row =  this.renderRow(cm, ds, rowIndex);
6506                 
6507                 tbody.createChild(row);
6508                 
6509                 var _this = this;
6510                 
6511                 if(row.cellObjects.length){
6512                     Roo.each(row.cellObjects, function(r){
6513                         _this.renderCellObject(r);
6514                     })
6515                 }
6516                 
6517             }, this);
6518         }
6519         
6520         Roo.each(this.el.select('tbody td', true).elements, function(e){
6521             e.on('mouseover', _this.onMouseover, _this);
6522         });
6523         
6524         Roo.each(this.el.select('tbody td', true).elements, function(e){
6525             e.on('mouseout', _this.onMouseout, _this);
6526         });
6527         this.fireEvent('rowsrendered', this);
6528         //if(this.loadMask){
6529         //    this.maskEl.hide();
6530         //}
6531         
6532         this.autoSize();
6533     },
6534     
6535     
6536     onUpdate : function(ds,record)
6537     {
6538         this.refreshRow(record);
6539         this.autoSize();
6540     },
6541     
6542     onRemove : function(ds, record, index, isUpdate){
6543         if(isUpdate !== true){
6544             this.fireEvent("beforerowremoved", this, index, record);
6545         }
6546         var bt = this.mainBody.dom;
6547         
6548         var rows = this.el.select('tbody > tr', true).elements;
6549         
6550         if(typeof(rows[index]) != 'undefined'){
6551             bt.removeChild(rows[index].dom);
6552         }
6553         
6554 //        if(bt.rows[index]){
6555 //            bt.removeChild(bt.rows[index]);
6556 //        }
6557         
6558         if(isUpdate !== true){
6559             //this.stripeRows(index);
6560             //this.syncRowHeights(index, index);
6561             //this.layout();
6562             this.fireEvent("rowremoved", this, index, record);
6563         }
6564     },
6565     
6566     onAdd : function(ds, records, rowIndex)
6567     {
6568         //Roo.log('on Add called');
6569         // - note this does not handle multiple adding very well..
6570         var bt = this.mainBody.dom;
6571         for (var i =0 ; i < records.length;i++) {
6572             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6573             //Roo.log(records[i]);
6574             //Roo.log(this.store.getAt(rowIndex+i));
6575             this.insertRow(this.store, rowIndex + i, false);
6576             return;
6577         }
6578         
6579     },
6580     
6581     
6582     refreshRow : function(record){
6583         var ds = this.store, index;
6584         if(typeof record == 'number'){
6585             index = record;
6586             record = ds.getAt(index);
6587         }else{
6588             index = ds.indexOf(record);
6589         }
6590         this.insertRow(ds, index, true);
6591         this.autoSize();
6592         this.onRemove(ds, record, index+1, true);
6593         this.autoSize();
6594         //this.syncRowHeights(index, index);
6595         //this.layout();
6596         this.fireEvent("rowupdated", this, index, record);
6597     },
6598     
6599     insertRow : function(dm, rowIndex, isUpdate){
6600         
6601         if(!isUpdate){
6602             this.fireEvent("beforerowsinserted", this, rowIndex);
6603         }
6604             //var s = this.getScrollState();
6605         var row = this.renderRow(this.cm, this.store, rowIndex);
6606         // insert before rowIndex..
6607         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6608         
6609         var _this = this;
6610                 
6611         if(row.cellObjects.length){
6612             Roo.each(row.cellObjects, function(r){
6613                 _this.renderCellObject(r);
6614             })
6615         }
6616             
6617         if(!isUpdate){
6618             this.fireEvent("rowsinserted", this, rowIndex);
6619             //this.syncRowHeights(firstRow, lastRow);
6620             //this.stripeRows(firstRow);
6621             //this.layout();
6622         }
6623         
6624     },
6625     
6626     
6627     getRowDom : function(rowIndex)
6628     {
6629         var rows = this.el.select('tbody > tr', true).elements;
6630         
6631         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6632         
6633     },
6634     // returns the object tree for a tr..
6635   
6636     
6637     renderRow : function(cm, ds, rowIndex) 
6638     {
6639         
6640         var d = ds.getAt(rowIndex);
6641         
6642         var row = {
6643             tag : 'tr',
6644             cn : []
6645         };
6646             
6647         var cellObjects = [];
6648         
6649         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6650             var config = cm.config[i];
6651             
6652             var renderer = cm.getRenderer(i);
6653             var value = '';
6654             var id = false;
6655             
6656             if(typeof(renderer) !== 'undefined'){
6657                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6658             }
6659             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6660             // and are rendered into the cells after the row is rendered - using the id for the element.
6661             
6662             if(typeof(value) === 'object'){
6663                 id = Roo.id();
6664                 cellObjects.push({
6665                     container : id,
6666                     cfg : value 
6667                 })
6668             }
6669             
6670             var rowcfg = {
6671                 record: d,
6672                 rowIndex : rowIndex,
6673                 colIndex : i,
6674                 rowClass : ''
6675             };
6676
6677             this.fireEvent('rowclass', this, rowcfg);
6678             
6679             var td = {
6680                 tag: 'td',
6681                 cls : rowcfg.rowClass,
6682                 style: '',
6683                 html: (typeof(value) === 'object') ? '' : value
6684             };
6685             
6686             if (id) {
6687                 td.id = id;
6688             }
6689             
6690             if(typeof(config.colspan) != 'undefined'){
6691                 td.colspan = config.colspan;
6692             }
6693             
6694             if(typeof(config.hidden) != 'undefined' && config.hidden){
6695                 td.style += ' display:none;';
6696             }
6697             
6698             if(typeof(config.align) != 'undefined' && config.align.length){
6699                 td.style += ' text-align:' + config.align + ';';
6700             }
6701             
6702             if(typeof(config.width) != 'undefined'){
6703                 td.style += ' width:' +  config.width + 'px;';
6704             }
6705             
6706             if(typeof(config.cursor) != 'undefined'){
6707                 td.style += ' cursor:' +  config.cursor + ';';
6708             }
6709             
6710             if(typeof(config.cls) != 'undefined'){
6711                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6712             }
6713             
6714             ['xs','sm','md','lg'].map(function(size){
6715                 
6716                 if(typeof(config[size]) == 'undefined'){
6717                     return;
6718                 }
6719                 
6720                 if (!config[size]) { // 0 = hidden
6721                     td.cls += ' hidden-' + size;
6722                     return;
6723                 }
6724                 
6725                 td.cls += ' col-' + size + '-' + config[size];
6726
6727             });
6728              
6729             row.cn.push(td);
6730            
6731         }
6732         
6733         row.cellObjects = cellObjects;
6734         
6735         return row;
6736           
6737     },
6738     
6739     
6740     
6741     onBeforeLoad : function()
6742     {
6743         //Roo.log('ds onBeforeLoad');
6744         
6745         //this.clear();
6746         
6747         //if(this.loadMask){
6748         //    this.maskEl.show();
6749         //}
6750     },
6751      /**
6752      * Remove all rows
6753      */
6754     clear : function()
6755     {
6756         this.el.select('tbody', true).first().dom.innerHTML = '';
6757     },
6758     /**
6759      * Show or hide a row.
6760      * @param {Number} rowIndex to show or hide
6761      * @param {Boolean} state hide
6762      */
6763     setRowVisibility : function(rowIndex, state)
6764     {
6765         var bt = this.mainBody.dom;
6766         
6767         var rows = this.el.select('tbody > tr', true).elements;
6768         
6769         if(typeof(rows[rowIndex]) == 'undefined'){
6770             return;
6771         }
6772         rows[rowIndex].dom.style.display = state ? '' : 'none';
6773     },
6774     
6775     
6776     getSelectionModel : function(){
6777         if(!this.selModel){
6778             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6779         }
6780         return this.selModel;
6781     },
6782     /*
6783      * Render the Roo.bootstrap object from renderder
6784      */
6785     renderCellObject : function(r)
6786     {
6787         var _this = this;
6788         
6789         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6790         
6791         var t = r.cfg.render(r.container);
6792         
6793         if(r.cfg.cn){
6794             Roo.each(r.cfg.cn, function(c){
6795                 var child = {
6796                     container: t.getChildContainer(),
6797                     cfg: c
6798                 };
6799                 _this.renderCellObject(child);
6800             })
6801         }
6802     },
6803     
6804     getRowIndex : function(row)
6805     {
6806         var rowIndex = -1;
6807         
6808         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6809             if(el != row){
6810                 return;
6811             }
6812             
6813             rowIndex = index;
6814         });
6815         
6816         return rowIndex;
6817     },
6818      /**
6819      * Returns the grid's underlying element = used by panel.Grid
6820      * @return {Element} The element
6821      */
6822     getGridEl : function(){
6823         return this.el;
6824     },
6825      /**
6826      * Forces a resize - used by panel.Grid
6827      * @return {Element} The element
6828      */
6829     autoSize : function()
6830     {
6831         //var ctr = Roo.get(this.container.dom.parentElement);
6832         var ctr = Roo.get(this.el.dom);
6833         
6834         var thd = this.getGridEl().select('thead',true).first();
6835         var tbd = this.getGridEl().select('tbody', true).first();
6836         var tfd = this.getGridEl().select('tfoot', true).first();
6837         
6838         var cw = ctr.getWidth();
6839         
6840         if (tbd) {
6841             
6842             tbd.setSize(ctr.getWidth(),
6843                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6844             );
6845             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6846             cw -= barsize;
6847         }
6848         cw = Math.max(cw, this.totalWidth);
6849         this.getGridEl().select('tr',true).setWidth(cw);
6850         // resize 'expandable coloumn?
6851         
6852         return; // we doe not have a view in this design..
6853         
6854     },
6855     onBodyScroll: function()
6856     {
6857         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6858         this.mainHead.setStyle({
6859             'position' : 'relative',
6860             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6861         });
6862         
6863         if(this.lazyLoad){
6864             
6865             var scrollHeight = this.mainBody.dom.scrollHeight;
6866             
6867             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6868             
6869             var height = this.mainBody.getHeight();
6870             
6871             if(scrollHeight - height == scrollTop) {
6872                 
6873                 var total = this.ds.getTotalCount();
6874                 
6875                 if(this.footer.cursor + this.footer.pageSize < total){
6876                     
6877                     this.footer.ds.load({
6878                         params : {
6879                             start : this.footer.cursor + this.footer.pageSize,
6880                             limit : this.footer.pageSize
6881                         },
6882                         add : true
6883                     });
6884                 }
6885             }
6886             
6887         }
6888     }
6889 });
6890
6891  
6892
6893  /*
6894  * - LGPL
6895  *
6896  * table cell
6897  * 
6898  */
6899
6900 /**
6901  * @class Roo.bootstrap.TableCell
6902  * @extends Roo.bootstrap.Component
6903  * Bootstrap TableCell class
6904  * @cfg {String} html cell contain text
6905  * @cfg {String} cls cell class
6906  * @cfg {String} tag cell tag (td|th) default td
6907  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6908  * @cfg {String} align Aligns the content in a cell
6909  * @cfg {String} axis Categorizes cells
6910  * @cfg {String} bgcolor Specifies the background color of a cell
6911  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6912  * @cfg {Number} colspan Specifies the number of columns a cell should span
6913  * @cfg {String} headers Specifies one or more header cells a cell is related to
6914  * @cfg {Number} height Sets the height of a cell
6915  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6916  * @cfg {Number} rowspan Sets the number of rows a cell should span
6917  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6918  * @cfg {String} valign Vertical aligns the content in a cell
6919  * @cfg {Number} width Specifies the width of a cell
6920  * 
6921  * @constructor
6922  * Create a new TableCell
6923  * @param {Object} config The config object
6924  */
6925
6926 Roo.bootstrap.TableCell = function(config){
6927     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6928 };
6929
6930 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6931     
6932     html: false,
6933     cls: false,
6934     tag: false,
6935     abbr: false,
6936     align: false,
6937     axis: false,
6938     bgcolor: false,
6939     charoff: false,
6940     colspan: false,
6941     headers: false,
6942     height: false,
6943     nowrap: false,
6944     rowspan: false,
6945     scope: false,
6946     valign: false,
6947     width: false,
6948     
6949     
6950     getAutoCreate : function(){
6951         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6952         
6953         cfg = {
6954             tag: 'td'
6955         };
6956         
6957         if(this.tag){
6958             cfg.tag = this.tag;
6959         }
6960         
6961         if (this.html) {
6962             cfg.html=this.html
6963         }
6964         if (this.cls) {
6965             cfg.cls=this.cls
6966         }
6967         if (this.abbr) {
6968             cfg.abbr=this.abbr
6969         }
6970         if (this.align) {
6971             cfg.align=this.align
6972         }
6973         if (this.axis) {
6974             cfg.axis=this.axis
6975         }
6976         if (this.bgcolor) {
6977             cfg.bgcolor=this.bgcolor
6978         }
6979         if (this.charoff) {
6980             cfg.charoff=this.charoff
6981         }
6982         if (this.colspan) {
6983             cfg.colspan=this.colspan
6984         }
6985         if (this.headers) {
6986             cfg.headers=this.headers
6987         }
6988         if (this.height) {
6989             cfg.height=this.height
6990         }
6991         if (this.nowrap) {
6992             cfg.nowrap=this.nowrap
6993         }
6994         if (this.rowspan) {
6995             cfg.rowspan=this.rowspan
6996         }
6997         if (this.scope) {
6998             cfg.scope=this.scope
6999         }
7000         if (this.valign) {
7001             cfg.valign=this.valign
7002         }
7003         if (this.width) {
7004             cfg.width=this.width
7005         }
7006         
7007         
7008         return cfg;
7009     }
7010    
7011 });
7012
7013  
7014
7015  /*
7016  * - LGPL
7017  *
7018  * table row
7019  * 
7020  */
7021
7022 /**
7023  * @class Roo.bootstrap.TableRow
7024  * @extends Roo.bootstrap.Component
7025  * Bootstrap TableRow class
7026  * @cfg {String} cls row class
7027  * @cfg {String} align Aligns the content in a table row
7028  * @cfg {String} bgcolor Specifies a background color for a table row
7029  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7030  * @cfg {String} valign Vertical aligns the content in a table row
7031  * 
7032  * @constructor
7033  * Create a new TableRow
7034  * @param {Object} config The config object
7035  */
7036
7037 Roo.bootstrap.TableRow = function(config){
7038     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7039 };
7040
7041 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7042     
7043     cls: false,
7044     align: false,
7045     bgcolor: false,
7046     charoff: false,
7047     valign: false,
7048     
7049     getAutoCreate : function(){
7050         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7051         
7052         cfg = {
7053             tag: 'tr'
7054         };
7055             
7056         if(this.cls){
7057             cfg.cls = this.cls;
7058         }
7059         if(this.align){
7060             cfg.align = this.align;
7061         }
7062         if(this.bgcolor){
7063             cfg.bgcolor = this.bgcolor;
7064         }
7065         if(this.charoff){
7066             cfg.charoff = this.charoff;
7067         }
7068         if(this.valign){
7069             cfg.valign = this.valign;
7070         }
7071         
7072         return cfg;
7073     }
7074    
7075 });
7076
7077  
7078
7079  /*
7080  * - LGPL
7081  *
7082  * table body
7083  * 
7084  */
7085
7086 /**
7087  * @class Roo.bootstrap.TableBody
7088  * @extends Roo.bootstrap.Component
7089  * Bootstrap TableBody class
7090  * @cfg {String} cls element class
7091  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7092  * @cfg {String} align Aligns the content inside the element
7093  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7094  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7095  * 
7096  * @constructor
7097  * Create a new TableBody
7098  * @param {Object} config The config object
7099  */
7100
7101 Roo.bootstrap.TableBody = function(config){
7102     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7103 };
7104
7105 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7106     
7107     cls: false,
7108     tag: false,
7109     align: false,
7110     charoff: false,
7111     valign: false,
7112     
7113     getAutoCreate : function(){
7114         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7115         
7116         cfg = {
7117             tag: 'tbody'
7118         };
7119             
7120         if (this.cls) {
7121             cfg.cls=this.cls
7122         }
7123         if(this.tag){
7124             cfg.tag = this.tag;
7125         }
7126         
7127         if(this.align){
7128             cfg.align = this.align;
7129         }
7130         if(this.charoff){
7131             cfg.charoff = this.charoff;
7132         }
7133         if(this.valign){
7134             cfg.valign = this.valign;
7135         }
7136         
7137         return cfg;
7138     }
7139     
7140     
7141 //    initEvents : function()
7142 //    {
7143 //        
7144 //        if(!this.store){
7145 //            return;
7146 //        }
7147 //        
7148 //        this.store = Roo.factory(this.store, Roo.data);
7149 //        this.store.on('load', this.onLoad, this);
7150 //        
7151 //        this.store.load();
7152 //        
7153 //    },
7154 //    
7155 //    onLoad: function () 
7156 //    {   
7157 //        this.fireEvent('load', this);
7158 //    }
7159 //    
7160 //   
7161 });
7162
7163  
7164
7165  /*
7166  * Based on:
7167  * Ext JS Library 1.1.1
7168  * Copyright(c) 2006-2007, Ext JS, LLC.
7169  *
7170  * Originally Released Under LGPL - original licence link has changed is not relivant.
7171  *
7172  * Fork - LGPL
7173  * <script type="text/javascript">
7174  */
7175
7176 // as we use this in bootstrap.
7177 Roo.namespace('Roo.form');
7178  /**
7179  * @class Roo.form.Action
7180  * Internal Class used to handle form actions
7181  * @constructor
7182  * @param {Roo.form.BasicForm} el The form element or its id
7183  * @param {Object} config Configuration options
7184  */
7185
7186  
7187  
7188 // define the action interface
7189 Roo.form.Action = function(form, options){
7190     this.form = form;
7191     this.options = options || {};
7192 };
7193 /**
7194  * Client Validation Failed
7195  * @const 
7196  */
7197 Roo.form.Action.CLIENT_INVALID = 'client';
7198 /**
7199  * Server Validation Failed
7200  * @const 
7201  */
7202 Roo.form.Action.SERVER_INVALID = 'server';
7203  /**
7204  * Connect to Server Failed
7205  * @const 
7206  */
7207 Roo.form.Action.CONNECT_FAILURE = 'connect';
7208 /**
7209  * Reading Data from Server Failed
7210  * @const 
7211  */
7212 Roo.form.Action.LOAD_FAILURE = 'load';
7213
7214 Roo.form.Action.prototype = {
7215     type : 'default',
7216     failureType : undefined,
7217     response : undefined,
7218     result : undefined,
7219
7220     // interface method
7221     run : function(options){
7222
7223     },
7224
7225     // interface method
7226     success : function(response){
7227
7228     },
7229
7230     // interface method
7231     handleResponse : function(response){
7232
7233     },
7234
7235     // default connection failure
7236     failure : function(response){
7237         
7238         this.response = response;
7239         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7240         this.form.afterAction(this, false);
7241     },
7242
7243     processResponse : function(response){
7244         this.response = response;
7245         if(!response.responseText){
7246             return true;
7247         }
7248         this.result = this.handleResponse(response);
7249         return this.result;
7250     },
7251
7252     // utility functions used internally
7253     getUrl : function(appendParams){
7254         var url = this.options.url || this.form.url || this.form.el.dom.action;
7255         if(appendParams){
7256             var p = this.getParams();
7257             if(p){
7258                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7259             }
7260         }
7261         return url;
7262     },
7263
7264     getMethod : function(){
7265         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7266     },
7267
7268     getParams : function(){
7269         var bp = this.form.baseParams;
7270         var p = this.options.params;
7271         if(p){
7272             if(typeof p == "object"){
7273                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7274             }else if(typeof p == 'string' && bp){
7275                 p += '&' + Roo.urlEncode(bp);
7276             }
7277         }else if(bp){
7278             p = Roo.urlEncode(bp);
7279         }
7280         return p;
7281     },
7282
7283     createCallback : function(){
7284         return {
7285             success: this.success,
7286             failure: this.failure,
7287             scope: this,
7288             timeout: (this.form.timeout*1000),
7289             upload: this.form.fileUpload ? this.success : undefined
7290         };
7291     }
7292 };
7293
7294 Roo.form.Action.Submit = function(form, options){
7295     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7296 };
7297
7298 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7299     type : 'submit',
7300
7301     haveProgress : false,
7302     uploadComplete : false,
7303     
7304     // uploadProgress indicator.
7305     uploadProgress : function()
7306     {
7307         if (!this.form.progressUrl) {
7308             return;
7309         }
7310         
7311         if (!this.haveProgress) {
7312             Roo.MessageBox.progress("Uploading", "Uploading");
7313         }
7314         if (this.uploadComplete) {
7315            Roo.MessageBox.hide();
7316            return;
7317         }
7318         
7319         this.haveProgress = true;
7320    
7321         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7322         
7323         var c = new Roo.data.Connection();
7324         c.request({
7325             url : this.form.progressUrl,
7326             params: {
7327                 id : uid
7328             },
7329             method: 'GET',
7330             success : function(req){
7331                //console.log(data);
7332                 var rdata = false;
7333                 var edata;
7334                 try  {
7335                    rdata = Roo.decode(req.responseText)
7336                 } catch (e) {
7337                     Roo.log("Invalid data from server..");
7338                     Roo.log(edata);
7339                     return;
7340                 }
7341                 if (!rdata || !rdata.success) {
7342                     Roo.log(rdata);
7343                     Roo.MessageBox.alert(Roo.encode(rdata));
7344                     return;
7345                 }
7346                 var data = rdata.data;
7347                 
7348                 if (this.uploadComplete) {
7349                    Roo.MessageBox.hide();
7350                    return;
7351                 }
7352                    
7353                 if (data){
7354                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7355                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7356                     );
7357                 }
7358                 this.uploadProgress.defer(2000,this);
7359             },
7360        
7361             failure: function(data) {
7362                 Roo.log('progress url failed ');
7363                 Roo.log(data);
7364             },
7365             scope : this
7366         });
7367            
7368     },
7369     
7370     
7371     run : function()
7372     {
7373         // run get Values on the form, so it syncs any secondary forms.
7374         this.form.getValues();
7375         
7376         var o = this.options;
7377         var method = this.getMethod();
7378         var isPost = method == 'POST';
7379         if(o.clientValidation === false || this.form.isValid()){
7380             
7381             if (this.form.progressUrl) {
7382                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7383                     (new Date() * 1) + '' + Math.random());
7384                     
7385             } 
7386             
7387             
7388             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7389                 form:this.form.el.dom,
7390                 url:this.getUrl(!isPost),
7391                 method: method,
7392                 params:isPost ? this.getParams() : null,
7393                 isUpload: this.form.fileUpload
7394             }));
7395             
7396             this.uploadProgress();
7397
7398         }else if (o.clientValidation !== false){ // client validation failed
7399             this.failureType = Roo.form.Action.CLIENT_INVALID;
7400             this.form.afterAction(this, false);
7401         }
7402     },
7403
7404     success : function(response)
7405     {
7406         this.uploadComplete= true;
7407         if (this.haveProgress) {
7408             Roo.MessageBox.hide();
7409         }
7410         
7411         
7412         var result = this.processResponse(response);
7413         if(result === true || result.success){
7414             this.form.afterAction(this, true);
7415             return;
7416         }
7417         if(result.errors){
7418             this.form.markInvalid(result.errors);
7419             this.failureType = Roo.form.Action.SERVER_INVALID;
7420         }
7421         this.form.afterAction(this, false);
7422     },
7423     failure : function(response)
7424     {
7425         this.uploadComplete= true;
7426         if (this.haveProgress) {
7427             Roo.MessageBox.hide();
7428         }
7429         
7430         this.response = response;
7431         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7432         this.form.afterAction(this, false);
7433     },
7434     
7435     handleResponse : function(response){
7436         if(this.form.errorReader){
7437             var rs = this.form.errorReader.read(response);
7438             var errors = [];
7439             if(rs.records){
7440                 for(var i = 0, len = rs.records.length; i < len; i++) {
7441                     var r = rs.records[i];
7442                     errors[i] = r.data;
7443                 }
7444             }
7445             if(errors.length < 1){
7446                 errors = null;
7447             }
7448             return {
7449                 success : rs.success,
7450                 errors : errors
7451             };
7452         }
7453         var ret = false;
7454         try {
7455             ret = Roo.decode(response.responseText);
7456         } catch (e) {
7457             ret = {
7458                 success: false,
7459                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7460                 errors : []
7461             };
7462         }
7463         return ret;
7464         
7465     }
7466 });
7467
7468
7469 Roo.form.Action.Load = function(form, options){
7470     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7471     this.reader = this.form.reader;
7472 };
7473
7474 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7475     type : 'load',
7476
7477     run : function(){
7478         
7479         Roo.Ajax.request(Roo.apply(
7480                 this.createCallback(), {
7481                     method:this.getMethod(),
7482                     url:this.getUrl(false),
7483                     params:this.getParams()
7484         }));
7485     },
7486
7487     success : function(response){
7488         
7489         var result = this.processResponse(response);
7490         if(result === true || !result.success || !result.data){
7491             this.failureType = Roo.form.Action.LOAD_FAILURE;
7492             this.form.afterAction(this, false);
7493             return;
7494         }
7495         this.form.clearInvalid();
7496         this.form.setValues(result.data);
7497         this.form.afterAction(this, true);
7498     },
7499
7500     handleResponse : function(response){
7501         if(this.form.reader){
7502             var rs = this.form.reader.read(response);
7503             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7504             return {
7505                 success : rs.success,
7506                 data : data
7507             };
7508         }
7509         return Roo.decode(response.responseText);
7510     }
7511 });
7512
7513 Roo.form.Action.ACTION_TYPES = {
7514     'load' : Roo.form.Action.Load,
7515     'submit' : Roo.form.Action.Submit
7516 };/*
7517  * - LGPL
7518  *
7519  * form
7520  *
7521  */
7522
7523 /**
7524  * @class Roo.bootstrap.Form
7525  * @extends Roo.bootstrap.Component
7526  * Bootstrap Form class
7527  * @cfg {String} method  GET | POST (default POST)
7528  * @cfg {String} labelAlign top | left (default top)
7529  * @cfg {String} align left  | right - for navbars
7530  * @cfg {Boolean} loadMask load mask when submit (default true)
7531
7532  *
7533  * @constructor
7534  * Create a new Form
7535  * @param {Object} config The config object
7536  */
7537
7538
7539 Roo.bootstrap.Form = function(config){
7540     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7541     
7542     Roo.bootstrap.Form.popover.apply();
7543     
7544     this.addEvents({
7545         /**
7546          * @event clientvalidation
7547          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7548          * @param {Form} this
7549          * @param {Boolean} valid true if the form has passed client-side validation
7550          */
7551         clientvalidation: true,
7552         /**
7553          * @event beforeaction
7554          * Fires before any action is performed. Return false to cancel the action.
7555          * @param {Form} this
7556          * @param {Action} action The action to be performed
7557          */
7558         beforeaction: true,
7559         /**
7560          * @event actionfailed
7561          * Fires when an action fails.
7562          * @param {Form} this
7563          * @param {Action} action The action that failed
7564          */
7565         actionfailed : true,
7566         /**
7567          * @event actioncomplete
7568          * Fires when an action is completed.
7569          * @param {Form} this
7570          * @param {Action} action The action that completed
7571          */
7572         actioncomplete : true
7573     });
7574
7575 };
7576
7577 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7578
7579      /**
7580      * @cfg {String} method
7581      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7582      */
7583     method : 'POST',
7584     /**
7585      * @cfg {String} url
7586      * The URL to use for form actions if one isn't supplied in the action options.
7587      */
7588     /**
7589      * @cfg {Boolean} fileUpload
7590      * Set to true if this form is a file upload.
7591      */
7592
7593     /**
7594      * @cfg {Object} baseParams
7595      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7596      */
7597
7598     /**
7599      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7600      */
7601     timeout: 30,
7602     /**
7603      * @cfg {Sting} align (left|right) for navbar forms
7604      */
7605     align : 'left',
7606
7607     // private
7608     activeAction : null,
7609
7610     /**
7611      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7612      * element by passing it or its id or mask the form itself by passing in true.
7613      * @type Mixed
7614      */
7615     waitMsgTarget : false,
7616
7617     loadMask : true,
7618     
7619     /**
7620      * @cfg {Boolean} errorMask (true|false) default false
7621      */
7622     errorMask : false,
7623     
7624     /**
7625      * @cfg {Number} maskOffset Default 100
7626      */
7627     maskOffset : 100,
7628     
7629     /**
7630      * @cfg {Boolean} maskBody
7631      */
7632     maskBody : false,
7633
7634     getAutoCreate : function(){
7635
7636         var cfg = {
7637             tag: 'form',
7638             method : this.method || 'POST',
7639             id : this.id || Roo.id(),
7640             cls : ''
7641         };
7642         if (this.parent().xtype.match(/^Nav/)) {
7643             cfg.cls = 'navbar-form navbar-' + this.align;
7644
7645         }
7646
7647         if (this.labelAlign == 'left' ) {
7648             cfg.cls += ' form-horizontal';
7649         }
7650
7651
7652         return cfg;
7653     },
7654     initEvents : function()
7655     {
7656         this.el.on('submit', this.onSubmit, this);
7657         // this was added as random key presses on the form where triggering form submit.
7658         this.el.on('keypress', function(e) {
7659             if (e.getCharCode() != 13) {
7660                 return true;
7661             }
7662             // we might need to allow it for textareas.. and some other items.
7663             // check e.getTarget().
7664
7665             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7666                 return true;
7667             }
7668
7669             Roo.log("keypress blocked");
7670
7671             e.preventDefault();
7672             return false;
7673         });
7674         
7675     },
7676     // private
7677     onSubmit : function(e){
7678         e.stopEvent();
7679     },
7680
7681      /**
7682      * Returns true if client-side validation on the form is successful.
7683      * @return Boolean
7684      */
7685     isValid : function(){
7686         var items = this.getItems();
7687         var valid = true;
7688         var target = false;
7689         
7690         items.each(function(f){
7691             if(f.validate()){
7692                 return;
7693             }
7694             valid = false;
7695
7696             if(!target && f.el.isVisible(true)){
7697                 target = f;
7698             }
7699            
7700         });
7701         
7702         if(this.errorMask && !valid){
7703             Roo.bootstrap.Form.popover.mask(this, target);
7704         }
7705         
7706         return valid;
7707     },
7708     
7709     /**
7710      * Returns true if any fields in this form have changed since their original load.
7711      * @return Boolean
7712      */
7713     isDirty : function(){
7714         var dirty = false;
7715         var items = this.getItems();
7716         items.each(function(f){
7717            if(f.isDirty()){
7718                dirty = true;
7719                return false;
7720            }
7721            return true;
7722         });
7723         return dirty;
7724     },
7725      /**
7726      * Performs a predefined action (submit or load) or custom actions you define on this form.
7727      * @param {String} actionName The name of the action type
7728      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7729      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7730      * accept other config options):
7731      * <pre>
7732 Property          Type             Description
7733 ----------------  ---------------  ----------------------------------------------------------------------------------
7734 url               String           The url for the action (defaults to the form's url)
7735 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7736 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7737 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7738                                    validate the form on the client (defaults to false)
7739      * </pre>
7740      * @return {BasicForm} this
7741      */
7742     doAction : function(action, options){
7743         if(typeof action == 'string'){
7744             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7745         }
7746         if(this.fireEvent('beforeaction', this, action) !== false){
7747             this.beforeAction(action);
7748             action.run.defer(100, action);
7749         }
7750         return this;
7751     },
7752
7753     // private
7754     beforeAction : function(action){
7755         var o = action.options;
7756         
7757         if(this.loadMask){
7758             
7759             if(this.maskBody){
7760                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7761             } else {
7762                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7763             }
7764         }
7765         // not really supported yet.. ??
7766
7767         //if(this.waitMsgTarget === true){
7768         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7769         //}else if(this.waitMsgTarget){
7770         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7771         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7772         //}else {
7773         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7774        // }
7775
7776     },
7777
7778     // private
7779     afterAction : function(action, success){
7780         this.activeAction = null;
7781         var o = action.options;
7782
7783         if(this.loadMask){
7784             
7785             if(this.maskBody){
7786                 Roo.get(document.body).unmask();
7787             } else {
7788                 this.el.unmask();
7789             }
7790         }
7791         
7792         //if(this.waitMsgTarget === true){
7793 //            this.el.unmask();
7794         //}else if(this.waitMsgTarget){
7795         //    this.waitMsgTarget.unmask();
7796         //}else{
7797         //    Roo.MessageBox.updateProgress(1);
7798         //    Roo.MessageBox.hide();
7799        // }
7800         //
7801         if(success){
7802             if(o.reset){
7803                 this.reset();
7804             }
7805             Roo.callback(o.success, o.scope, [this, action]);
7806             this.fireEvent('actioncomplete', this, action);
7807
7808         }else{
7809
7810             // failure condition..
7811             // we have a scenario where updates need confirming.
7812             // eg. if a locking scenario exists..
7813             // we look for { errors : { needs_confirm : true }} in the response.
7814             if (
7815                 (typeof(action.result) != 'undefined')  &&
7816                 (typeof(action.result.errors) != 'undefined')  &&
7817                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7818            ){
7819                 var _t = this;
7820                 Roo.log("not supported yet");
7821                  /*
7822
7823                 Roo.MessageBox.confirm(
7824                     "Change requires confirmation",
7825                     action.result.errorMsg,
7826                     function(r) {
7827                         if (r != 'yes') {
7828                             return;
7829                         }
7830                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7831                     }
7832
7833                 );
7834                 */
7835
7836
7837                 return;
7838             }
7839
7840             Roo.callback(o.failure, o.scope, [this, action]);
7841             // show an error message if no failed handler is set..
7842             if (!this.hasListener('actionfailed')) {
7843                 Roo.log("need to add dialog support");
7844                 /*
7845                 Roo.MessageBox.alert("Error",
7846                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7847                         action.result.errorMsg :
7848                         "Saving Failed, please check your entries or try again"
7849                 );
7850                 */
7851             }
7852
7853             this.fireEvent('actionfailed', this, action);
7854         }
7855
7856     },
7857     /**
7858      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7859      * @param {String} id The value to search for
7860      * @return Field
7861      */
7862     findField : function(id){
7863         var items = this.getItems();
7864         var field = items.get(id);
7865         if(!field){
7866              items.each(function(f){
7867                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7868                     field = f;
7869                     return false;
7870                 }
7871                 return true;
7872             });
7873         }
7874         return field || null;
7875     },
7876      /**
7877      * Mark fields in this form invalid in bulk.
7878      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7879      * @return {BasicForm} this
7880      */
7881     markInvalid : function(errors){
7882         if(errors instanceof Array){
7883             for(var i = 0, len = errors.length; i < len; i++){
7884                 var fieldError = errors[i];
7885                 var f = this.findField(fieldError.id);
7886                 if(f){
7887                     f.markInvalid(fieldError.msg);
7888                 }
7889             }
7890         }else{
7891             var field, id;
7892             for(id in errors){
7893                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7894                     field.markInvalid(errors[id]);
7895                 }
7896             }
7897         }
7898         //Roo.each(this.childForms || [], function (f) {
7899         //    f.markInvalid(errors);
7900         //});
7901
7902         return this;
7903     },
7904
7905     /**
7906      * Set values for fields in this form in bulk.
7907      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7908      * @return {BasicForm} this
7909      */
7910     setValues : function(values){
7911         if(values instanceof Array){ // array of objects
7912             for(var i = 0, len = values.length; i < len; i++){
7913                 var v = values[i];
7914                 var f = this.findField(v.id);
7915                 if(f){
7916                     f.setValue(v.value);
7917                     if(this.trackResetOnLoad){
7918                         f.originalValue = f.getValue();
7919                     }
7920                 }
7921             }
7922         }else{ // object hash
7923             var field, id;
7924             for(id in values){
7925                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7926
7927                     if (field.setFromData &&
7928                         field.valueField &&
7929                         field.displayField &&
7930                         // combos' with local stores can
7931                         // be queried via setValue()
7932                         // to set their value..
7933                         (field.store && !field.store.isLocal)
7934                         ) {
7935                         // it's a combo
7936                         var sd = { };
7937                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7938                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7939                         field.setFromData(sd);
7940
7941                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7942                         
7943                         field.setFromData(values);
7944                         
7945                     } else {
7946                         field.setValue(values[id]);
7947                     }
7948
7949
7950                     if(this.trackResetOnLoad){
7951                         field.originalValue = field.getValue();
7952                     }
7953                 }
7954             }
7955         }
7956
7957         //Roo.each(this.childForms || [], function (f) {
7958         //    f.setValues(values);
7959         //});
7960
7961         return this;
7962     },
7963
7964     /**
7965      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7966      * they are returned as an array.
7967      * @param {Boolean} asString
7968      * @return {Object}
7969      */
7970     getValues : function(asString){
7971         //if (this.childForms) {
7972             // copy values from the child forms
7973         //    Roo.each(this.childForms, function (f) {
7974         //        this.setValues(f.getValues());
7975         //    }, this);
7976         //}
7977
7978
7979
7980         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7981         if(asString === true){
7982             return fs;
7983         }
7984         return Roo.urlDecode(fs);
7985     },
7986
7987     /**
7988      * Returns the fields in this form as an object with key/value pairs.
7989      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7990      * @return {Object}
7991      */
7992     getFieldValues : function(with_hidden)
7993     {
7994         var items = this.getItems();
7995         var ret = {};
7996         items.each(function(f){
7997             
7998             if (!f.getName()) {
7999                 return;
8000             }
8001             
8002             var v = f.getValue();
8003             
8004             if (f.inputType =='radio') {
8005                 if (typeof(ret[f.getName()]) == 'undefined') {
8006                     ret[f.getName()] = ''; // empty..
8007                 }
8008
8009                 if (!f.el.dom.checked) {
8010                     return;
8011
8012                 }
8013                 v = f.el.dom.value;
8014
8015             }
8016             
8017             if(f.xtype == 'MoneyField'){
8018                 ret[f.currencyName] = f.getCurrency();
8019             }
8020
8021             // not sure if this supported any more..
8022             if ((typeof(v) == 'object') && f.getRawValue) {
8023                 v = f.getRawValue() ; // dates..
8024             }
8025             // combo boxes where name != hiddenName...
8026             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8027                 ret[f.name] = f.getRawValue();
8028             }
8029             ret[f.getName()] = v;
8030         });
8031
8032         return ret;
8033     },
8034
8035     /**
8036      * Clears all invalid messages in this form.
8037      * @return {BasicForm} this
8038      */
8039     clearInvalid : function(){
8040         var items = this.getItems();
8041
8042         items.each(function(f){
8043            f.clearInvalid();
8044         });
8045
8046
8047
8048         return this;
8049     },
8050
8051     /**
8052      * Resets this form.
8053      * @return {BasicForm} this
8054      */
8055     reset : function(){
8056         var items = this.getItems();
8057         items.each(function(f){
8058             f.reset();
8059         });
8060
8061         Roo.each(this.childForms || [], function (f) {
8062             f.reset();
8063         });
8064
8065
8066         return this;
8067     },
8068     
8069     getItems : function()
8070     {
8071         var r=new Roo.util.MixedCollection(false, function(o){
8072             return o.id || (o.id = Roo.id());
8073         });
8074         var iter = function(el) {
8075             if (el.inputEl) {
8076                 r.add(el);
8077             }
8078             if (!el.items) {
8079                 return;
8080             }
8081             Roo.each(el.items,function(e) {
8082                 iter(e);
8083             });
8084
8085
8086         };
8087
8088         iter(this);
8089         return r;
8090         
8091     }
8092
8093 });
8094
8095 Roo.apply(Roo.bootstrap.Form, {
8096     
8097     popover : {
8098         
8099         padding : 5,
8100         
8101         isApplied : false,
8102         
8103         isMasked : false,
8104         
8105         form : false,
8106         
8107         target : false,
8108         
8109         toolTip : false,
8110         
8111         intervalID : false,
8112         
8113         maskEl : false,
8114         
8115         apply : function()
8116         {
8117             if(this.isApplied){
8118                 return;
8119             }
8120             
8121             this.maskEl = {
8122                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8123                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8124                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8125                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8126             };
8127             
8128             this.maskEl.top.enableDisplayMode("block");
8129             this.maskEl.left.enableDisplayMode("block");
8130             this.maskEl.bottom.enableDisplayMode("block");
8131             this.maskEl.right.enableDisplayMode("block");
8132             
8133             this.toolTip = new Roo.bootstrap.Tooltip({
8134                 cls : 'roo-form-error-popover',
8135                 alignment : {
8136                     'left' : ['r-l', [-2,0], 'right'],
8137                     'right' : ['l-r', [2,0], 'left'],
8138                     'bottom' : ['tl-bl', [0,2], 'top'],
8139                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8140                 }
8141             });
8142             
8143             this.toolTip.render(Roo.get(document.body));
8144
8145             this.toolTip.el.enableDisplayMode("block");
8146             
8147             Roo.get(document.body).on('click', function(){
8148                 this.unmask();
8149             }, this);
8150             
8151             Roo.get(document.body).on('touchstart', function(){
8152                 this.unmask();
8153             }, this);
8154             
8155             this.isApplied = true
8156         },
8157         
8158         mask : function(form, target)
8159         {
8160             this.form = form;
8161             
8162             this.target = target;
8163             
8164             if(!this.form.errorMask || !target.el){
8165                 return;
8166             }
8167             
8168             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8169             
8170             Roo.log(scrollable);
8171             
8172             var ot = this.target.el.calcOffsetsTo(scrollable);
8173             
8174             var scrollTo = ot[1] - this.form.maskOffset;
8175             
8176             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8177             
8178             scrollable.scrollTo('top', scrollTo);
8179             
8180             var box = this.target.el.getBox();
8181             Roo.log(box);
8182             var zIndex = Roo.bootstrap.Modal.zIndex++;
8183
8184             
8185             this.maskEl.top.setStyle('position', 'absolute');
8186             this.maskEl.top.setStyle('z-index', zIndex);
8187             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8188             this.maskEl.top.setLeft(0);
8189             this.maskEl.top.setTop(0);
8190             this.maskEl.top.show();
8191             
8192             this.maskEl.left.setStyle('position', 'absolute');
8193             this.maskEl.left.setStyle('z-index', zIndex);
8194             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8195             this.maskEl.left.setLeft(0);
8196             this.maskEl.left.setTop(box.y - this.padding);
8197             this.maskEl.left.show();
8198
8199             this.maskEl.bottom.setStyle('position', 'absolute');
8200             this.maskEl.bottom.setStyle('z-index', zIndex);
8201             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8202             this.maskEl.bottom.setLeft(0);
8203             this.maskEl.bottom.setTop(box.bottom + this.padding);
8204             this.maskEl.bottom.show();
8205
8206             this.maskEl.right.setStyle('position', 'absolute');
8207             this.maskEl.right.setStyle('z-index', zIndex);
8208             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8209             this.maskEl.right.setLeft(box.right + this.padding);
8210             this.maskEl.right.setTop(box.y - this.padding);
8211             this.maskEl.right.show();
8212
8213             this.toolTip.bindEl = this.target.el;
8214
8215             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8216
8217             var tip = this.target.blankText;
8218
8219             if(this.target.getValue() !== '' ) {
8220                 
8221                 if (this.target.invalidText.length) {
8222                     tip = this.target.invalidText;
8223                 } else if (this.target.regexText.length){
8224                     tip = this.target.regexText;
8225                 }
8226             }
8227
8228             this.toolTip.show(tip);
8229
8230             this.intervalID = window.setInterval(function() {
8231                 Roo.bootstrap.Form.popover.unmask();
8232             }, 10000);
8233
8234             window.onwheel = function(){ return false;};
8235             
8236             (function(){ this.isMasked = true; }).defer(500, this);
8237             
8238         },
8239         
8240         unmask : function()
8241         {
8242             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8243                 return;
8244             }
8245             
8246             this.maskEl.top.setStyle('position', 'absolute');
8247             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8248             this.maskEl.top.hide();
8249
8250             this.maskEl.left.setStyle('position', 'absolute');
8251             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8252             this.maskEl.left.hide();
8253
8254             this.maskEl.bottom.setStyle('position', 'absolute');
8255             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8256             this.maskEl.bottom.hide();
8257
8258             this.maskEl.right.setStyle('position', 'absolute');
8259             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8260             this.maskEl.right.hide();
8261             
8262             this.toolTip.hide();
8263             
8264             this.toolTip.el.hide();
8265             
8266             window.onwheel = function(){ return true;};
8267             
8268             if(this.intervalID){
8269                 window.clearInterval(this.intervalID);
8270                 this.intervalID = false;
8271             }
8272             
8273             this.isMasked = false;
8274             
8275         }
8276         
8277     }
8278     
8279 });
8280
8281 /*
8282  * Based on:
8283  * Ext JS Library 1.1.1
8284  * Copyright(c) 2006-2007, Ext JS, LLC.
8285  *
8286  * Originally Released Under LGPL - original licence link has changed is not relivant.
8287  *
8288  * Fork - LGPL
8289  * <script type="text/javascript">
8290  */
8291 /**
8292  * @class Roo.form.VTypes
8293  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8294  * @singleton
8295  */
8296 Roo.form.VTypes = function(){
8297     // closure these in so they are only created once.
8298     var alpha = /^[a-zA-Z_]+$/;
8299     var alphanum = /^[a-zA-Z0-9_]+$/;
8300     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8301     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8302
8303     // All these messages and functions are configurable
8304     return {
8305         /**
8306          * The function used to validate email addresses
8307          * @param {String} value The email address
8308          */
8309         'email' : function(v){
8310             return email.test(v);
8311         },
8312         /**
8313          * The error text to display when the email validation function returns false
8314          * @type String
8315          */
8316         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8317         /**
8318          * The keystroke filter mask to be applied on email input
8319          * @type RegExp
8320          */
8321         'emailMask' : /[a-z0-9_\.\-@]/i,
8322
8323         /**
8324          * The function used to validate URLs
8325          * @param {String} value The URL
8326          */
8327         'url' : function(v){
8328             return url.test(v);
8329         },
8330         /**
8331          * The error text to display when the url validation function returns false
8332          * @type String
8333          */
8334         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8335         
8336         /**
8337          * The function used to validate alpha values
8338          * @param {String} value The value
8339          */
8340         'alpha' : function(v){
8341             return alpha.test(v);
8342         },
8343         /**
8344          * The error text to display when the alpha validation function returns false
8345          * @type String
8346          */
8347         'alphaText' : 'This field should only contain letters and _',
8348         /**
8349          * The keystroke filter mask to be applied on alpha input
8350          * @type RegExp
8351          */
8352         'alphaMask' : /[a-z_]/i,
8353
8354         /**
8355          * The function used to validate alphanumeric values
8356          * @param {String} value The value
8357          */
8358         'alphanum' : function(v){
8359             return alphanum.test(v);
8360         },
8361         /**
8362          * The error text to display when the alphanumeric validation function returns false
8363          * @type String
8364          */
8365         'alphanumText' : 'This field should only contain letters, numbers and _',
8366         /**
8367          * The keystroke filter mask to be applied on alphanumeric input
8368          * @type RegExp
8369          */
8370         'alphanumMask' : /[a-z0-9_]/i
8371     };
8372 }();/*
8373  * - LGPL
8374  *
8375  * Input
8376  * 
8377  */
8378
8379 /**
8380  * @class Roo.bootstrap.Input
8381  * @extends Roo.bootstrap.Component
8382  * Bootstrap Input class
8383  * @cfg {Boolean} disabled is it disabled
8384  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8385  * @cfg {String} name name of the input
8386  * @cfg {string} fieldLabel - the label associated
8387  * @cfg {string} placeholder - placeholder to put in text.
8388  * @cfg {string}  before - input group add on before
8389  * @cfg {string} after - input group add on after
8390  * @cfg {string} size - (lg|sm) or leave empty..
8391  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8392  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8393  * @cfg {Number} md colspan out of 12 for computer-sized screens
8394  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8395  * @cfg {string} value default value of the input
8396  * @cfg {Number} labelWidth set the width of label 
8397  * @cfg {Number} labellg set the width of label (1-12)
8398  * @cfg {Number} labelmd set the width of label (1-12)
8399  * @cfg {Number} labelsm set the width of label (1-12)
8400  * @cfg {Number} labelxs set the width of label (1-12)
8401  * @cfg {String} labelAlign (top|left)
8402  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8403  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8404  * @cfg {String} indicatorpos (left|right) default left
8405
8406  * @cfg {String} align (left|center|right) Default left
8407  * @cfg {Boolean} forceFeedback (true|false) Default false
8408  * 
8409  * 
8410  * 
8411  * 
8412  * @constructor
8413  * Create a new Input
8414  * @param {Object} config The config object
8415  */
8416
8417 Roo.bootstrap.Input = function(config){
8418     
8419     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8420     
8421     this.addEvents({
8422         /**
8423          * @event focus
8424          * Fires when this field receives input focus.
8425          * @param {Roo.form.Field} this
8426          */
8427         focus : true,
8428         /**
8429          * @event blur
8430          * Fires when this field loses input focus.
8431          * @param {Roo.form.Field} this
8432          */
8433         blur : true,
8434         /**
8435          * @event specialkey
8436          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8437          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8438          * @param {Roo.form.Field} this
8439          * @param {Roo.EventObject} e The event object
8440          */
8441         specialkey : true,
8442         /**
8443          * @event change
8444          * Fires just before the field blurs if the field value has changed.
8445          * @param {Roo.form.Field} this
8446          * @param {Mixed} newValue The new value
8447          * @param {Mixed} oldValue The original value
8448          */
8449         change : true,
8450         /**
8451          * @event invalid
8452          * Fires after the field has been marked as invalid.
8453          * @param {Roo.form.Field} this
8454          * @param {String} msg The validation message
8455          */
8456         invalid : true,
8457         /**
8458          * @event valid
8459          * Fires after the field has been validated with no errors.
8460          * @param {Roo.form.Field} this
8461          */
8462         valid : true,
8463          /**
8464          * @event keyup
8465          * Fires after the key up
8466          * @param {Roo.form.Field} this
8467          * @param {Roo.EventObject}  e The event Object
8468          */
8469         keyup : true
8470     });
8471 };
8472
8473 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8474      /**
8475      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8476       automatic validation (defaults to "keyup").
8477      */
8478     validationEvent : "keyup",
8479      /**
8480      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8481      */
8482     validateOnBlur : true,
8483     /**
8484      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8485      */
8486     validationDelay : 250,
8487      /**
8488      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8489      */
8490     focusClass : "x-form-focus",  // not needed???
8491     
8492        
8493     /**
8494      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8495      */
8496     invalidClass : "has-warning",
8497     
8498     /**
8499      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8500      */
8501     validClass : "has-success",
8502     
8503     /**
8504      * @cfg {Boolean} hasFeedback (true|false) default true
8505      */
8506     hasFeedback : true,
8507     
8508     /**
8509      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8510      */
8511     invalidFeedbackClass : "glyphicon-warning-sign",
8512     
8513     /**
8514      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8515      */
8516     validFeedbackClass : "glyphicon-ok",
8517     
8518     /**
8519      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8520      */
8521     selectOnFocus : false,
8522     
8523      /**
8524      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8525      */
8526     maskRe : null,
8527        /**
8528      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8529      */
8530     vtype : null,
8531     
8532       /**
8533      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8534      */
8535     disableKeyFilter : false,
8536     
8537        /**
8538      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8539      */
8540     disabled : false,
8541      /**
8542      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8543      */
8544     allowBlank : true,
8545     /**
8546      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8547      */
8548     blankText : "Please complete this mandatory field",
8549     
8550      /**
8551      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8552      */
8553     minLength : 0,
8554     /**
8555      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8556      */
8557     maxLength : Number.MAX_VALUE,
8558     /**
8559      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8560      */
8561     minLengthText : "The minimum length for this field is {0}",
8562     /**
8563      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8564      */
8565     maxLengthText : "The maximum length for this field is {0}",
8566   
8567     
8568     /**
8569      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8570      * If available, this function will be called only after the basic validators all return true, and will be passed the
8571      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8572      */
8573     validator : null,
8574     /**
8575      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8576      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8577      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8578      */
8579     regex : null,
8580     /**
8581      * @cfg {String} regexText -- Depricated - use Invalid Text
8582      */
8583     regexText : "",
8584     
8585     /**
8586      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8587      */
8588     invalidText : "",
8589     
8590     
8591     
8592     autocomplete: false,
8593     
8594     
8595     fieldLabel : '',
8596     inputType : 'text',
8597     
8598     name : false,
8599     placeholder: false,
8600     before : false,
8601     after : false,
8602     size : false,
8603     hasFocus : false,
8604     preventMark: false,
8605     isFormField : true,
8606     value : '',
8607     labelWidth : 2,
8608     labelAlign : false,
8609     readOnly : false,
8610     align : false,
8611     formatedValue : false,
8612     forceFeedback : false,
8613     
8614     indicatorpos : 'left',
8615     
8616     labellg : 0,
8617     labelmd : 0,
8618     labelsm : 0,
8619     labelxs : 0,
8620     
8621     parentLabelAlign : function()
8622     {
8623         var parent = this;
8624         while (parent.parent()) {
8625             parent = parent.parent();
8626             if (typeof(parent.labelAlign) !='undefined') {
8627                 return parent.labelAlign;
8628             }
8629         }
8630         return 'left';
8631         
8632     },
8633     
8634     getAutoCreate : function()
8635     {
8636         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8637         
8638         var id = Roo.id();
8639         
8640         var cfg = {};
8641         
8642         if(this.inputType != 'hidden'){
8643             cfg.cls = 'form-group' //input-group
8644         }
8645         
8646         var input =  {
8647             tag: 'input',
8648             id : id,
8649             type : this.inputType,
8650             value : this.value,
8651             cls : 'form-control',
8652             placeholder : this.placeholder || '',
8653             autocomplete : this.autocomplete || 'new-password'
8654         };
8655         
8656         if(this.align){
8657             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8658         }
8659         
8660         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8661             input.maxLength = this.maxLength;
8662         }
8663         
8664         if (this.disabled) {
8665             input.disabled=true;
8666         }
8667         
8668         if (this.readOnly) {
8669             input.readonly=true;
8670         }
8671         
8672         if (this.name) {
8673             input.name = this.name;
8674         }
8675         
8676         if (this.size) {
8677             input.cls += ' input-' + this.size;
8678         }
8679         
8680         var settings=this;
8681         ['xs','sm','md','lg'].map(function(size){
8682             if (settings[size]) {
8683                 cfg.cls += ' col-' + size + '-' + settings[size];
8684             }
8685         });
8686         
8687         var inputblock = input;
8688         
8689         var feedback = {
8690             tag: 'span',
8691             cls: 'glyphicon form-control-feedback'
8692         };
8693             
8694         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8695             
8696             inputblock = {
8697                 cls : 'has-feedback',
8698                 cn :  [
8699                     input,
8700                     feedback
8701                 ] 
8702             };  
8703         }
8704         
8705         if (this.before || this.after) {
8706             
8707             inputblock = {
8708                 cls : 'input-group',
8709                 cn :  [] 
8710             };
8711             
8712             if (this.before && typeof(this.before) == 'string') {
8713                 
8714                 inputblock.cn.push({
8715                     tag :'span',
8716                     cls : 'roo-input-before input-group-addon',
8717                     html : this.before
8718                 });
8719             }
8720             if (this.before && typeof(this.before) == 'object') {
8721                 this.before = Roo.factory(this.before);
8722                 
8723                 inputblock.cn.push({
8724                     tag :'span',
8725                     cls : 'roo-input-before input-group-' +
8726                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8727                 });
8728             }
8729             
8730             inputblock.cn.push(input);
8731             
8732             if (this.after && typeof(this.after) == 'string') {
8733                 inputblock.cn.push({
8734                     tag :'span',
8735                     cls : 'roo-input-after input-group-addon',
8736                     html : this.after
8737                 });
8738             }
8739             if (this.after && typeof(this.after) == 'object') {
8740                 this.after = Roo.factory(this.after);
8741                 
8742                 inputblock.cn.push({
8743                     tag :'span',
8744                     cls : 'roo-input-after input-group-' +
8745                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8746                 });
8747             }
8748             
8749             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8750                 inputblock.cls += ' has-feedback';
8751                 inputblock.cn.push(feedback);
8752             }
8753         };
8754         
8755         if (align ==='left' && this.fieldLabel.length) {
8756             
8757             cfg.cls += ' roo-form-group-label-left';
8758             
8759             cfg.cn = [
8760                 {
8761                     tag : 'i',
8762                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8763                     tooltip : 'This field is required'
8764                 },
8765                 {
8766                     tag: 'label',
8767                     'for' :  id,
8768                     cls : 'control-label',
8769                     html : this.fieldLabel
8770
8771                 },
8772                 {
8773                     cls : "", 
8774                     cn: [
8775                         inputblock
8776                     ]
8777                 }
8778             ];
8779             
8780             var labelCfg = cfg.cn[1];
8781             var contentCfg = cfg.cn[2];
8782             
8783             if(this.indicatorpos == 'right'){
8784                 cfg.cn = [
8785                     {
8786                         tag: 'label',
8787                         'for' :  id,
8788                         cls : 'control-label',
8789                         cn : [
8790                             {
8791                                 tag : 'span',
8792                                 html : this.fieldLabel
8793                             },
8794                             {
8795                                 tag : 'i',
8796                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8797                                 tooltip : 'This field is required'
8798                             }
8799                         ]
8800                     },
8801                     {
8802                         cls : "",
8803                         cn: [
8804                             inputblock
8805                         ]
8806                     }
8807
8808                 ];
8809                 
8810                 labelCfg = cfg.cn[0];
8811                 contentCfg = cfg.cn[1];
8812             
8813             }
8814             
8815             if(this.labelWidth > 12){
8816                 labelCfg.style = "width: " + this.labelWidth + 'px';
8817             }
8818             
8819             if(this.labelWidth < 13 && this.labelmd == 0){
8820                 this.labelmd = this.labelWidth;
8821             }
8822             
8823             if(this.labellg > 0){
8824                 labelCfg.cls += ' col-lg-' + this.labellg;
8825                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8826             }
8827             
8828             if(this.labelmd > 0){
8829                 labelCfg.cls += ' col-md-' + this.labelmd;
8830                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8831             }
8832             
8833             if(this.labelsm > 0){
8834                 labelCfg.cls += ' col-sm-' + this.labelsm;
8835                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8836             }
8837             
8838             if(this.labelxs > 0){
8839                 labelCfg.cls += ' col-xs-' + this.labelxs;
8840                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8841             }
8842             
8843             
8844         } else if ( this.fieldLabel.length) {
8845                 
8846             cfg.cn = [
8847                 {
8848                     tag : 'i',
8849                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8850                     tooltip : 'This field is required'
8851                 },
8852                 {
8853                     tag: 'label',
8854                    //cls : 'input-group-addon',
8855                     html : this.fieldLabel
8856
8857                 },
8858
8859                inputblock
8860
8861            ];
8862            
8863            if(this.indicatorpos == 'right'){
8864                 
8865                 cfg.cn = [
8866                     {
8867                         tag: 'label',
8868                        //cls : 'input-group-addon',
8869                         html : this.fieldLabel
8870
8871                     },
8872                     {
8873                         tag : 'i',
8874                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8875                         tooltip : 'This field is required'
8876                     },
8877
8878                    inputblock
8879
8880                ];
8881
8882             }
8883
8884         } else {
8885             
8886             cfg.cn = [
8887
8888                     inputblock
8889
8890             ];
8891                 
8892                 
8893         };
8894         
8895         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8896            cfg.cls += ' navbar-form';
8897         }
8898         
8899         if (this.parentType === 'NavGroup') {
8900            cfg.cls += ' navbar-form';
8901            cfg.tag = 'li';
8902         }
8903         
8904         return cfg;
8905         
8906     },
8907     /**
8908      * return the real input element.
8909      */
8910     inputEl: function ()
8911     {
8912         return this.el.select('input.form-control',true).first();
8913     },
8914     
8915     tooltipEl : function()
8916     {
8917         return this.inputEl();
8918     },
8919     
8920     indicatorEl : function()
8921     {
8922         var indicator = this.el.select('i.roo-required-indicator',true).first();
8923         
8924         if(!indicator){
8925             return false;
8926         }
8927         
8928         return indicator;
8929         
8930     },
8931     
8932     setDisabled : function(v)
8933     {
8934         var i  = this.inputEl().dom;
8935         if (!v) {
8936             i.removeAttribute('disabled');
8937             return;
8938             
8939         }
8940         i.setAttribute('disabled','true');
8941     },
8942     initEvents : function()
8943     {
8944           
8945         this.inputEl().on("keydown" , this.fireKey,  this);
8946         this.inputEl().on("focus", this.onFocus,  this);
8947         this.inputEl().on("blur", this.onBlur,  this);
8948         
8949         this.inputEl().relayEvent('keyup', this);
8950         
8951         this.indicator = this.indicatorEl();
8952         
8953         if(this.indicator){
8954             this.indicator.addClass('invisible');
8955             
8956         }
8957  
8958         // reference to original value for reset
8959         this.originalValue = this.getValue();
8960         //Roo.form.TextField.superclass.initEvents.call(this);
8961         if(this.validationEvent == 'keyup'){
8962             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8963             this.inputEl().on('keyup', this.filterValidation, this);
8964         }
8965         else if(this.validationEvent !== false){
8966             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8967         }
8968         
8969         if(this.selectOnFocus){
8970             this.on("focus", this.preFocus, this);
8971             
8972         }
8973         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8974             this.inputEl().on("keypress", this.filterKeys, this);
8975         } else {
8976             this.inputEl().relayEvent('keypress', this);
8977         }
8978        /* if(this.grow){
8979             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8980             this.el.on("click", this.autoSize,  this);
8981         }
8982         */
8983         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8984             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8985         }
8986         
8987         if (typeof(this.before) == 'object') {
8988             this.before.render(this.el.select('.roo-input-before',true).first());
8989         }
8990         if (typeof(this.after) == 'object') {
8991             this.after.render(this.el.select('.roo-input-after',true).first());
8992         }
8993         
8994         
8995     },
8996     filterValidation : function(e){
8997         if(!e.isNavKeyPress()){
8998             this.validationTask.delay(this.validationDelay);
8999         }
9000     },
9001      /**
9002      * Validates the field value
9003      * @return {Boolean} True if the value is valid, else false
9004      */
9005     validate : function(){
9006         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9007         if(this.disabled || this.validateValue(this.getRawValue())){
9008             this.markValid();
9009             return true;
9010         }
9011         
9012         this.markInvalid();
9013         return false;
9014     },
9015     
9016     
9017     /**
9018      * Validates a value according to the field's validation rules and marks the field as invalid
9019      * if the validation fails
9020      * @param {Mixed} value The value to validate
9021      * @return {Boolean} True if the value is valid, else false
9022      */
9023     validateValue : function(value){
9024         if(value.length < 1)  { // if it's blank
9025             if(this.allowBlank){
9026                 return true;
9027             }            
9028             return this.inputEl().hasClass('hide') ? true : false;
9029         }
9030         
9031         if(value.length < this.minLength){
9032             return false;
9033         }
9034         if(value.length > this.maxLength){
9035             return false;
9036         }
9037         if(this.vtype){
9038             var vt = Roo.form.VTypes;
9039             if(!vt[this.vtype](value, this)){
9040                 return false;
9041             }
9042         }
9043         if(typeof this.validator == "function"){
9044             var msg = this.validator(value);
9045             if(msg !== true){
9046                 return false;
9047             }
9048             if (typeof(msg) == 'string') {
9049                 this.invalidText = msg;
9050             }
9051         }
9052         
9053         if(this.regex && !this.regex.test(value)){
9054             return false;
9055         }
9056         
9057         return true;
9058     },
9059
9060     
9061     
9062      // private
9063     fireKey : function(e){
9064         //Roo.log('field ' + e.getKey());
9065         if(e.isNavKeyPress()){
9066             this.fireEvent("specialkey", this, e);
9067         }
9068     },
9069     focus : function (selectText){
9070         if(this.rendered){
9071             this.inputEl().focus();
9072             if(selectText === true){
9073                 this.inputEl().dom.select();
9074             }
9075         }
9076         return this;
9077     } ,
9078     
9079     onFocus : function(){
9080         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9081            // this.el.addClass(this.focusClass);
9082         }
9083         if(!this.hasFocus){
9084             this.hasFocus = true;
9085             this.startValue = this.getValue();
9086             this.fireEvent("focus", this);
9087         }
9088     },
9089     
9090     beforeBlur : Roo.emptyFn,
9091
9092     
9093     // private
9094     onBlur : function(){
9095         this.beforeBlur();
9096         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9097             //this.el.removeClass(this.focusClass);
9098         }
9099         this.hasFocus = false;
9100         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9101             this.validate();
9102         }
9103         var v = this.getValue();
9104         if(String(v) !== String(this.startValue)){
9105             this.fireEvent('change', this, v, this.startValue);
9106         }
9107         this.fireEvent("blur", this);
9108     },
9109     
9110     /**
9111      * Resets the current field value to the originally loaded value and clears any validation messages
9112      */
9113     reset : function(){
9114         this.setValue(this.originalValue);
9115         this.validate();
9116     },
9117      /**
9118      * Returns the name of the field
9119      * @return {Mixed} name The name field
9120      */
9121     getName: function(){
9122         return this.name;
9123     },
9124      /**
9125      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9126      * @return {Mixed} value The field value
9127      */
9128     getValue : function(){
9129         
9130         var v = this.inputEl().getValue();
9131         
9132         return v;
9133     },
9134     /**
9135      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9136      * @return {Mixed} value The field value
9137      */
9138     getRawValue : function(){
9139         var v = this.inputEl().getValue();
9140         
9141         return v;
9142     },
9143     
9144     /**
9145      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9146      * @param {Mixed} value The value to set
9147      */
9148     setRawValue : function(v){
9149         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9150     },
9151     
9152     selectText : function(start, end){
9153         var v = this.getRawValue();
9154         if(v.length > 0){
9155             start = start === undefined ? 0 : start;
9156             end = end === undefined ? v.length : end;
9157             var d = this.inputEl().dom;
9158             if(d.setSelectionRange){
9159                 d.setSelectionRange(start, end);
9160             }else if(d.createTextRange){
9161                 var range = d.createTextRange();
9162                 range.moveStart("character", start);
9163                 range.moveEnd("character", v.length-end);
9164                 range.select();
9165             }
9166         }
9167     },
9168     
9169     /**
9170      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9171      * @param {Mixed} value The value to set
9172      */
9173     setValue : function(v){
9174         this.value = v;
9175         if(this.rendered){
9176             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9177             this.validate();
9178         }
9179     },
9180     
9181     /*
9182     processValue : function(value){
9183         if(this.stripCharsRe){
9184             var newValue = value.replace(this.stripCharsRe, '');
9185             if(newValue !== value){
9186                 this.setRawValue(newValue);
9187                 return newValue;
9188             }
9189         }
9190         return value;
9191     },
9192   */
9193     preFocus : function(){
9194         
9195         if(this.selectOnFocus){
9196             this.inputEl().dom.select();
9197         }
9198     },
9199     filterKeys : function(e){
9200         var k = e.getKey();
9201         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9202             return;
9203         }
9204         var c = e.getCharCode(), cc = String.fromCharCode(c);
9205         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9206             return;
9207         }
9208         if(!this.maskRe.test(cc)){
9209             e.stopEvent();
9210         }
9211     },
9212      /**
9213      * Clear any invalid styles/messages for this field
9214      */
9215     clearInvalid : function(){
9216         
9217         if(!this.el || this.preventMark){ // not rendered
9218             return;
9219         }
9220         
9221      
9222         this.el.removeClass(this.invalidClass);
9223         
9224         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9225             
9226             var feedback = this.el.select('.form-control-feedback', true).first();
9227             
9228             if(feedback){
9229                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9230             }
9231             
9232         }
9233         
9234         this.fireEvent('valid', this);
9235     },
9236     
9237      /**
9238      * Mark this field as valid
9239      */
9240     markValid : function()
9241     {
9242         if(!this.el  || this.preventMark){ // not rendered...
9243             return;
9244         }
9245         
9246         this.el.removeClass([this.invalidClass, this.validClass]);
9247         
9248         var feedback = this.el.select('.form-control-feedback', true).first();
9249             
9250         if(feedback){
9251             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9252         }
9253
9254         if(this.disabled){
9255             return;
9256         }
9257         
9258         if(this.allowBlank && !this.getRawValue().length){
9259             return;
9260         }
9261         
9262         if(this.indicator){
9263             this.indicator.removeClass('visible');
9264             this.indicator.addClass('invisible');
9265         }
9266         
9267         this.el.addClass(this.validClass);
9268         
9269         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9270             
9271             var feedback = this.el.select('.form-control-feedback', true).first();
9272             
9273             if(feedback){
9274                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9275                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9276             }
9277             
9278         }
9279         
9280         this.fireEvent('valid', this);
9281     },
9282     
9283      /**
9284      * Mark this field as invalid
9285      * @param {String} msg The validation message
9286      */
9287     markInvalid : function(msg)
9288     {
9289         if(!this.el  || this.preventMark){ // not rendered
9290             return;
9291         }
9292         
9293         this.el.removeClass([this.invalidClass, this.validClass]);
9294         
9295         var feedback = this.el.select('.form-control-feedback', true).first();
9296             
9297         if(feedback){
9298             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9299         }
9300
9301         if(this.disabled){
9302             return;
9303         }
9304         
9305         if(this.allowBlank && !this.getRawValue().length){
9306             return;
9307         }
9308         
9309         if(this.indicator){
9310             this.indicator.removeClass('invisible');
9311             this.indicator.addClass('visible');
9312         }
9313         
9314         this.el.addClass(this.invalidClass);
9315         
9316         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9317             
9318             var feedback = this.el.select('.form-control-feedback', true).first();
9319             
9320             if(feedback){
9321                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9322                 
9323                 if(this.getValue().length || this.forceFeedback){
9324                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9325                 }
9326                 
9327             }
9328             
9329         }
9330         
9331         this.fireEvent('invalid', this, msg);
9332     },
9333     // private
9334     SafariOnKeyDown : function(event)
9335     {
9336         // this is a workaround for a password hang bug on chrome/ webkit.
9337         if (this.inputEl().dom.type != 'password') {
9338             return;
9339         }
9340         
9341         var isSelectAll = false;
9342         
9343         if(this.inputEl().dom.selectionEnd > 0){
9344             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9345         }
9346         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9347             event.preventDefault();
9348             this.setValue('');
9349             return;
9350         }
9351         
9352         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9353             
9354             event.preventDefault();
9355             // this is very hacky as keydown always get's upper case.
9356             //
9357             var cc = String.fromCharCode(event.getCharCode());
9358             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9359             
9360         }
9361     },
9362     adjustWidth : function(tag, w){
9363         tag = tag.toLowerCase();
9364         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9365             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9366                 if(tag == 'input'){
9367                     return w + 2;
9368                 }
9369                 if(tag == 'textarea'){
9370                     return w-2;
9371                 }
9372             }else if(Roo.isOpera){
9373                 if(tag == 'input'){
9374                     return w + 2;
9375                 }
9376                 if(tag == 'textarea'){
9377                     return w-2;
9378                 }
9379             }
9380         }
9381         return w;
9382     },
9383     
9384     setFieldLabel : function(v)
9385     {
9386         if(!this.rendered){
9387             return;
9388         }
9389         
9390         this.fieldLabel = v;
9391         
9392         if(this.indicator){
9393             var ar = this.el.select('label > span',true);
9394             if (!ar.elements.length) {
9395                 Roo.log("could not find label > span on element");
9396                 Roo.log(this);
9397                 return;
9398             }
9399             this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9400             return;
9401         }
9402         
9403         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9404     }
9405 });
9406
9407  
9408 /*
9409  * - LGPL
9410  *
9411  * Input
9412  * 
9413  */
9414
9415 /**
9416  * @class Roo.bootstrap.TextArea
9417  * @extends Roo.bootstrap.Input
9418  * Bootstrap TextArea class
9419  * @cfg {Number} cols Specifies the visible width of a text area
9420  * @cfg {Number} rows Specifies the visible number of lines in a text area
9421  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9422  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9423  * @cfg {string} html text
9424  * 
9425  * @constructor
9426  * Create a new TextArea
9427  * @param {Object} config The config object
9428  */
9429
9430 Roo.bootstrap.TextArea = function(config){
9431     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9432    
9433 };
9434
9435 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9436      
9437     cols : false,
9438     rows : 5,
9439     readOnly : false,
9440     warp : 'soft',
9441     resize : false,
9442     value: false,
9443     html: false,
9444     
9445     getAutoCreate : function(){
9446         
9447         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9448         
9449         var id = Roo.id();
9450         
9451         var cfg = {};
9452         
9453         if(this.inputType != 'hidden'){
9454             cfg.cls = 'form-group' //input-group
9455         }
9456         
9457         var input =  {
9458             tag: 'textarea',
9459             id : id,
9460             warp : this.warp,
9461             rows : this.rows,
9462             value : this.value || '',
9463             html: this.html || '',
9464             cls : 'form-control',
9465             placeholder : this.placeholder || '' 
9466             
9467         };
9468         
9469         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9470             input.maxLength = this.maxLength;
9471         }
9472         
9473         if(this.resize){
9474             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9475         }
9476         
9477         if(this.cols){
9478             input.cols = this.cols;
9479         }
9480         
9481         if (this.readOnly) {
9482             input.readonly = true;
9483         }
9484         
9485         if (this.name) {
9486             input.name = this.name;
9487         }
9488         
9489         if (this.size) {
9490             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9491         }
9492         
9493         var settings=this;
9494         ['xs','sm','md','lg'].map(function(size){
9495             if (settings[size]) {
9496                 cfg.cls += ' col-' + size + '-' + settings[size];
9497             }
9498         });
9499         
9500         var inputblock = input;
9501         
9502         if(this.hasFeedback && !this.allowBlank){
9503             
9504             var feedback = {
9505                 tag: 'span',
9506                 cls: 'glyphicon form-control-feedback'
9507             };
9508
9509             inputblock = {
9510                 cls : 'has-feedback',
9511                 cn :  [
9512                     input,
9513                     feedback
9514                 ] 
9515             };  
9516         }
9517         
9518         
9519         if (this.before || this.after) {
9520             
9521             inputblock = {
9522                 cls : 'input-group',
9523                 cn :  [] 
9524             };
9525             if (this.before) {
9526                 inputblock.cn.push({
9527                     tag :'span',
9528                     cls : 'input-group-addon',
9529                     html : this.before
9530                 });
9531             }
9532             
9533             inputblock.cn.push(input);
9534             
9535             if(this.hasFeedback && !this.allowBlank){
9536                 inputblock.cls += ' has-feedback';
9537                 inputblock.cn.push(feedback);
9538             }
9539             
9540             if (this.after) {
9541                 inputblock.cn.push({
9542                     tag :'span',
9543                     cls : 'input-group-addon',
9544                     html : this.after
9545                 });
9546             }
9547             
9548         }
9549         
9550         if (align ==='left' && this.fieldLabel.length) {
9551             cfg.cn = [
9552                 {
9553                     tag: 'label',
9554                     'for' :  id,
9555                     cls : 'control-label',
9556                     html : this.fieldLabel
9557                 },
9558                 {
9559                     cls : "",
9560                     cn: [
9561                         inputblock
9562                     ]
9563                 }
9564
9565             ];
9566             
9567             if(this.labelWidth > 12){
9568                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9569             }
9570
9571             if(this.labelWidth < 13 && this.labelmd == 0){
9572                 this.labelmd = this.labelWidth;
9573             }
9574
9575             if(this.labellg > 0){
9576                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9577                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9578             }
9579
9580             if(this.labelmd > 0){
9581                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9582                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9583             }
9584
9585             if(this.labelsm > 0){
9586                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9587                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9588             }
9589
9590             if(this.labelxs > 0){
9591                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9592                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9593             }
9594             
9595         } else if ( this.fieldLabel.length) {
9596             cfg.cn = [
9597
9598                {
9599                    tag: 'label',
9600                    //cls : 'input-group-addon',
9601                    html : this.fieldLabel
9602
9603                },
9604
9605                inputblock
9606
9607            ];
9608
9609         } else {
9610
9611             cfg.cn = [
9612
9613                 inputblock
9614
9615             ];
9616                 
9617         }
9618         
9619         if (this.disabled) {
9620             input.disabled=true;
9621         }
9622         
9623         return cfg;
9624         
9625     },
9626     /**
9627      * return the real textarea element.
9628      */
9629     inputEl: function ()
9630     {
9631         return this.el.select('textarea.form-control',true).first();
9632     },
9633     
9634     /**
9635      * Clear any invalid styles/messages for this field
9636      */
9637     clearInvalid : function()
9638     {
9639         
9640         if(!this.el || this.preventMark){ // not rendered
9641             return;
9642         }
9643         
9644         var label = this.el.select('label', true).first();
9645         var icon = this.el.select('i.fa-star', true).first();
9646         
9647         if(label && icon){
9648             icon.remove();
9649         }
9650         
9651         this.el.removeClass(this.invalidClass);
9652         
9653         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9654             
9655             var feedback = this.el.select('.form-control-feedback', true).first();
9656             
9657             if(feedback){
9658                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9659             }
9660             
9661         }
9662         
9663         this.fireEvent('valid', this);
9664     },
9665     
9666      /**
9667      * Mark this field as valid
9668      */
9669     markValid : function()
9670     {
9671         if(!this.el  || this.preventMark){ // not rendered
9672             return;
9673         }
9674         
9675         this.el.removeClass([this.invalidClass, this.validClass]);
9676         
9677         var feedback = this.el.select('.form-control-feedback', true).first();
9678             
9679         if(feedback){
9680             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9681         }
9682
9683         if(this.disabled || this.allowBlank){
9684             return;
9685         }
9686         
9687         var label = this.el.select('label', true).first();
9688         var icon = this.el.select('i.fa-star', true).first();
9689         
9690         if(label && icon){
9691             icon.remove();
9692         }
9693         
9694         this.el.addClass(this.validClass);
9695         
9696         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9697             
9698             var feedback = this.el.select('.form-control-feedback', true).first();
9699             
9700             if(feedback){
9701                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9702                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9703             }
9704             
9705         }
9706         
9707         this.fireEvent('valid', this);
9708     },
9709     
9710      /**
9711      * Mark this field as invalid
9712      * @param {String} msg The validation message
9713      */
9714     markInvalid : function(msg)
9715     {
9716         if(!this.el  || this.preventMark){ // not rendered
9717             return;
9718         }
9719         
9720         this.el.removeClass([this.invalidClass, this.validClass]);
9721         
9722         var feedback = this.el.select('.form-control-feedback', true).first();
9723             
9724         if(feedback){
9725             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9726         }
9727
9728         if(this.disabled || this.allowBlank){
9729             return;
9730         }
9731         
9732         var label = this.el.select('label', true).first();
9733         var icon = this.el.select('i.fa-star', true).first();
9734         
9735         if(!this.getValue().length && label && !icon){
9736             this.el.createChild({
9737                 tag : 'i',
9738                 cls : 'text-danger fa fa-lg fa-star',
9739                 tooltip : 'This field is required',
9740                 style : 'margin-right:5px;'
9741             }, label, true);
9742         }
9743
9744         this.el.addClass(this.invalidClass);
9745         
9746         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9747             
9748             var feedback = this.el.select('.form-control-feedback', true).first();
9749             
9750             if(feedback){
9751                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9752                 
9753                 if(this.getValue().length || this.forceFeedback){
9754                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9755                 }
9756                 
9757             }
9758             
9759         }
9760         
9761         this.fireEvent('invalid', this, msg);
9762     }
9763 });
9764
9765  
9766 /*
9767  * - LGPL
9768  *
9769  * trigger field - base class for combo..
9770  * 
9771  */
9772  
9773 /**
9774  * @class Roo.bootstrap.TriggerField
9775  * @extends Roo.bootstrap.Input
9776  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9777  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9778  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9779  * for which you can provide a custom implementation.  For example:
9780  * <pre><code>
9781 var trigger = new Roo.bootstrap.TriggerField();
9782 trigger.onTriggerClick = myTriggerFn;
9783 trigger.applyTo('my-field');
9784 </code></pre>
9785  *
9786  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9787  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9788  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9789  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9790  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9791
9792  * @constructor
9793  * Create a new TriggerField.
9794  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9795  * to the base TextField)
9796  */
9797 Roo.bootstrap.TriggerField = function(config){
9798     this.mimicing = false;
9799     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9800 };
9801
9802 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9803     /**
9804      * @cfg {String} triggerClass A CSS class to apply to the trigger
9805      */
9806      /**
9807      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9808      */
9809     hideTrigger:false,
9810
9811     /**
9812      * @cfg {Boolean} removable (true|false) special filter default false
9813      */
9814     removable : false,
9815     
9816     /** @cfg {Boolean} grow @hide */
9817     /** @cfg {Number} growMin @hide */
9818     /** @cfg {Number} growMax @hide */
9819
9820     /**
9821      * @hide 
9822      * @method
9823      */
9824     autoSize: Roo.emptyFn,
9825     // private
9826     monitorTab : true,
9827     // private
9828     deferHeight : true,
9829
9830     
9831     actionMode : 'wrap',
9832     
9833     caret : false,
9834     
9835     
9836     getAutoCreate : function(){
9837        
9838         var align = this.labelAlign || this.parentLabelAlign();
9839         
9840         var id = Roo.id();
9841         
9842         var cfg = {
9843             cls: 'form-group' //input-group
9844         };
9845         
9846         
9847         var input =  {
9848             tag: 'input',
9849             id : id,
9850             type : this.inputType,
9851             cls : 'form-control',
9852             autocomplete: 'new-password',
9853             placeholder : this.placeholder || '' 
9854             
9855         };
9856         if (this.name) {
9857             input.name = this.name;
9858         }
9859         if (this.size) {
9860             input.cls += ' input-' + this.size;
9861         }
9862         
9863         if (this.disabled) {
9864             input.disabled=true;
9865         }
9866         
9867         var inputblock = input;
9868         
9869         if(this.hasFeedback && !this.allowBlank){
9870             
9871             var feedback = {
9872                 tag: 'span',
9873                 cls: 'glyphicon form-control-feedback'
9874             };
9875             
9876             if(this.removable && !this.editable && !this.tickable){
9877                 inputblock = {
9878                     cls : 'has-feedback',
9879                     cn :  [
9880                         inputblock,
9881                         {
9882                             tag: 'button',
9883                             html : 'x',
9884                             cls : 'roo-combo-removable-btn close'
9885                         },
9886                         feedback
9887                     ] 
9888                 };
9889             } else {
9890                 inputblock = {
9891                     cls : 'has-feedback',
9892                     cn :  [
9893                         inputblock,
9894                         feedback
9895                     ] 
9896                 };
9897             }
9898
9899         } else {
9900             if(this.removable && !this.editable && !this.tickable){
9901                 inputblock = {
9902                     cls : 'roo-removable',
9903                     cn :  [
9904                         inputblock,
9905                         {
9906                             tag: 'button',
9907                             html : 'x',
9908                             cls : 'roo-combo-removable-btn close'
9909                         }
9910                     ] 
9911                 };
9912             }
9913         }
9914         
9915         if (this.before || this.after) {
9916             
9917             inputblock = {
9918                 cls : 'input-group',
9919                 cn :  [] 
9920             };
9921             if (this.before) {
9922                 inputblock.cn.push({
9923                     tag :'span',
9924                     cls : 'input-group-addon',
9925                     html : this.before
9926                 });
9927             }
9928             
9929             inputblock.cn.push(input);
9930             
9931             if(this.hasFeedback && !this.allowBlank){
9932                 inputblock.cls += ' has-feedback';
9933                 inputblock.cn.push(feedback);
9934             }
9935             
9936             if (this.after) {
9937                 inputblock.cn.push({
9938                     tag :'span',
9939                     cls : 'input-group-addon',
9940                     html : this.after
9941                 });
9942             }
9943             
9944         };
9945         
9946         var box = {
9947             tag: 'div',
9948             cn: [
9949                 {
9950                     tag: 'input',
9951                     type : 'hidden',
9952                     cls: 'form-hidden-field'
9953                 },
9954                 inputblock
9955             ]
9956             
9957         };
9958         
9959         if(this.multiple){
9960             box = {
9961                 tag: 'div',
9962                 cn: [
9963                     {
9964                         tag: 'input',
9965                         type : 'hidden',
9966                         cls: 'form-hidden-field'
9967                     },
9968                     {
9969                         tag: 'ul',
9970                         cls: 'roo-select2-choices',
9971                         cn:[
9972                             {
9973                                 tag: 'li',
9974                                 cls: 'roo-select2-search-field',
9975                                 cn: [
9976
9977                                     inputblock
9978                                 ]
9979                             }
9980                         ]
9981                     }
9982                 ]
9983             }
9984         };
9985         
9986         var combobox = {
9987             cls: 'roo-select2-container input-group',
9988             cn: [
9989                 box
9990 //                {
9991 //                    tag: 'ul',
9992 //                    cls: 'typeahead typeahead-long dropdown-menu',
9993 //                    style: 'display:none'
9994 //                }
9995             ]
9996         };
9997         
9998         if(!this.multiple && this.showToggleBtn){
9999             
10000             var caret = {
10001                         tag: 'span',
10002                         cls: 'caret'
10003              };
10004             if (this.caret != false) {
10005                 caret = {
10006                      tag: 'i',
10007                      cls: 'fa fa-' + this.caret
10008                 };
10009                 
10010             }
10011             
10012             combobox.cn.push({
10013                 tag :'span',
10014                 cls : 'input-group-addon btn dropdown-toggle',
10015                 cn : [
10016                     caret,
10017                     {
10018                         tag: 'span',
10019                         cls: 'combobox-clear',
10020                         cn  : [
10021                             {
10022                                 tag : 'i',
10023                                 cls: 'icon-remove'
10024                             }
10025                         ]
10026                     }
10027                 ]
10028
10029             })
10030         }
10031         
10032         if(this.multiple){
10033             combobox.cls += ' roo-select2-container-multi';
10034         }
10035         
10036         if (align ==='left' && this.fieldLabel.length) {
10037             
10038             cfg.cls += ' roo-form-group-label-left';
10039
10040             cfg.cn = [
10041                 {
10042                     tag : 'i',
10043                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10044                     tooltip : 'This field is required'
10045                 },
10046                 {
10047                     tag: 'label',
10048                     'for' :  id,
10049                     cls : 'control-label',
10050                     html : this.fieldLabel
10051
10052                 },
10053                 {
10054                     cls : "", 
10055                     cn: [
10056                         combobox
10057                     ]
10058                 }
10059
10060             ];
10061             
10062             var labelCfg = cfg.cn[1];
10063             var contentCfg = cfg.cn[2];
10064             
10065             if(this.indicatorpos == 'right'){
10066                 cfg.cn = [
10067                     {
10068                         tag: 'label',
10069                         'for' :  id,
10070                         cls : 'control-label',
10071                         cn : [
10072                             {
10073                                 tag : 'span',
10074                                 html : this.fieldLabel
10075                             },
10076                             {
10077                                 tag : 'i',
10078                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10079                                 tooltip : 'This field is required'
10080                             }
10081                         ]
10082                     },
10083                     {
10084                         cls : "", 
10085                         cn: [
10086                             combobox
10087                         ]
10088                     }
10089
10090                 ];
10091                 
10092                 labelCfg = cfg.cn[0];
10093                 contentCfg = cfg.cn[1];
10094             }
10095             
10096             if(this.labelWidth > 12){
10097                 labelCfg.style = "width: " + this.labelWidth + 'px';
10098             }
10099             
10100             if(this.labelWidth < 13 && this.labelmd == 0){
10101                 this.labelmd = this.labelWidth;
10102             }
10103             
10104             if(this.labellg > 0){
10105                 labelCfg.cls += ' col-lg-' + this.labellg;
10106                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10107             }
10108             
10109             if(this.labelmd > 0){
10110                 labelCfg.cls += ' col-md-' + this.labelmd;
10111                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10112             }
10113             
10114             if(this.labelsm > 0){
10115                 labelCfg.cls += ' col-sm-' + this.labelsm;
10116                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10117             }
10118             
10119             if(this.labelxs > 0){
10120                 labelCfg.cls += ' col-xs-' + this.labelxs;
10121                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10122             }
10123             
10124         } else if ( this.fieldLabel.length) {
10125 //                Roo.log(" label");
10126             cfg.cn = [
10127                 {
10128                    tag : 'i',
10129                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10130                    tooltip : 'This field is required'
10131                },
10132                {
10133                    tag: 'label',
10134                    //cls : 'input-group-addon',
10135                    html : this.fieldLabel
10136
10137                },
10138
10139                combobox
10140
10141             ];
10142             
10143             if(this.indicatorpos == 'right'){
10144                 
10145                 cfg.cn = [
10146                     {
10147                        tag: 'label',
10148                        cn : [
10149                            {
10150                                tag : 'span',
10151                                html : this.fieldLabel
10152                            },
10153                            {
10154                               tag : 'i',
10155                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10156                               tooltip : 'This field is required'
10157                            }
10158                        ]
10159
10160                     },
10161                     combobox
10162
10163                 ];
10164
10165             }
10166
10167         } else {
10168             
10169 //                Roo.log(" no label && no align");
10170                 cfg = combobox
10171                      
10172                 
10173         }
10174         
10175         var settings=this;
10176         ['xs','sm','md','lg'].map(function(size){
10177             if (settings[size]) {
10178                 cfg.cls += ' col-' + size + '-' + settings[size];
10179             }
10180         });
10181         
10182         return cfg;
10183         
10184     },
10185     
10186     
10187     
10188     // private
10189     onResize : function(w, h){
10190 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10191 //        if(typeof w == 'number'){
10192 //            var x = w - this.trigger.getWidth();
10193 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10194 //            this.trigger.setStyle('left', x+'px');
10195 //        }
10196     },
10197
10198     // private
10199     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10200
10201     // private
10202     getResizeEl : function(){
10203         return this.inputEl();
10204     },
10205
10206     // private
10207     getPositionEl : function(){
10208         return this.inputEl();
10209     },
10210
10211     // private
10212     alignErrorIcon : function(){
10213         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10214     },
10215
10216     // private
10217     initEvents : function(){
10218         
10219         this.createList();
10220         
10221         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10222         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10223         if(!this.multiple && this.showToggleBtn){
10224             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10225             if(this.hideTrigger){
10226                 this.trigger.setDisplayed(false);
10227             }
10228             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10229         }
10230         
10231         if(this.multiple){
10232             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10233         }
10234         
10235         if(this.removable && !this.editable && !this.tickable){
10236             var close = this.closeTriggerEl();
10237             
10238             if(close){
10239                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10240                 close.on('click', this.removeBtnClick, this, close);
10241             }
10242         }
10243         
10244         //this.trigger.addClassOnOver('x-form-trigger-over');
10245         //this.trigger.addClassOnClick('x-form-trigger-click');
10246         
10247         //if(!this.width){
10248         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10249         //}
10250     },
10251     
10252     closeTriggerEl : function()
10253     {
10254         var close = this.el.select('.roo-combo-removable-btn', true).first();
10255         return close ? close : false;
10256     },
10257     
10258     removeBtnClick : function(e, h, el)
10259     {
10260         e.preventDefault();
10261         
10262         if(this.fireEvent("remove", this) !== false){
10263             this.reset();
10264             this.fireEvent("afterremove", this)
10265         }
10266     },
10267     
10268     createList : function()
10269     {
10270         this.list = Roo.get(document.body).createChild({
10271             tag: 'ul',
10272             cls: 'typeahead typeahead-long dropdown-menu',
10273             style: 'display:none'
10274         });
10275         
10276         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10277         
10278     },
10279
10280     // private
10281     initTrigger : function(){
10282        
10283     },
10284
10285     // private
10286     onDestroy : function(){
10287         if(this.trigger){
10288             this.trigger.removeAllListeners();
10289           //  this.trigger.remove();
10290         }
10291         //if(this.wrap){
10292         //    this.wrap.remove();
10293         //}
10294         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10295     },
10296
10297     // private
10298     onFocus : function(){
10299         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10300         /*
10301         if(!this.mimicing){
10302             this.wrap.addClass('x-trigger-wrap-focus');
10303             this.mimicing = true;
10304             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10305             if(this.monitorTab){
10306                 this.el.on("keydown", this.checkTab, this);
10307             }
10308         }
10309         */
10310     },
10311
10312     // private
10313     checkTab : function(e){
10314         if(e.getKey() == e.TAB){
10315             this.triggerBlur();
10316         }
10317     },
10318
10319     // private
10320     onBlur : function(){
10321         // do nothing
10322     },
10323
10324     // private
10325     mimicBlur : function(e, t){
10326         /*
10327         if(!this.wrap.contains(t) && this.validateBlur()){
10328             this.triggerBlur();
10329         }
10330         */
10331     },
10332
10333     // private
10334     triggerBlur : function(){
10335         this.mimicing = false;
10336         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10337         if(this.monitorTab){
10338             this.el.un("keydown", this.checkTab, this);
10339         }
10340         //this.wrap.removeClass('x-trigger-wrap-focus');
10341         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10342     },
10343
10344     // private
10345     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10346     validateBlur : function(e, t){
10347         return true;
10348     },
10349
10350     // private
10351     onDisable : function(){
10352         this.inputEl().dom.disabled = true;
10353         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10354         //if(this.wrap){
10355         //    this.wrap.addClass('x-item-disabled');
10356         //}
10357     },
10358
10359     // private
10360     onEnable : function(){
10361         this.inputEl().dom.disabled = false;
10362         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10363         //if(this.wrap){
10364         //    this.el.removeClass('x-item-disabled');
10365         //}
10366     },
10367
10368     // private
10369     onShow : function(){
10370         var ae = this.getActionEl();
10371         
10372         if(ae){
10373             ae.dom.style.display = '';
10374             ae.dom.style.visibility = 'visible';
10375         }
10376     },
10377
10378     // private
10379     
10380     onHide : function(){
10381         var ae = this.getActionEl();
10382         ae.dom.style.display = 'none';
10383     },
10384
10385     /**
10386      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10387      * by an implementing function.
10388      * @method
10389      * @param {EventObject} e
10390      */
10391     onTriggerClick : Roo.emptyFn
10392 });
10393  /*
10394  * Based on:
10395  * Ext JS Library 1.1.1
10396  * Copyright(c) 2006-2007, Ext JS, LLC.
10397  *
10398  * Originally Released Under LGPL - original licence link has changed is not relivant.
10399  *
10400  * Fork - LGPL
10401  * <script type="text/javascript">
10402  */
10403
10404
10405 /**
10406  * @class Roo.data.SortTypes
10407  * @singleton
10408  * Defines the default sorting (casting?) comparison functions used when sorting data.
10409  */
10410 Roo.data.SortTypes = {
10411     /**
10412      * Default sort that does nothing
10413      * @param {Mixed} s The value being converted
10414      * @return {Mixed} The comparison value
10415      */
10416     none : function(s){
10417         return s;
10418     },
10419     
10420     /**
10421      * The regular expression used to strip tags
10422      * @type {RegExp}
10423      * @property
10424      */
10425     stripTagsRE : /<\/?[^>]+>/gi,
10426     
10427     /**
10428      * Strips all HTML tags to sort on text only
10429      * @param {Mixed} s The value being converted
10430      * @return {String} The comparison value
10431      */
10432     asText : function(s){
10433         return String(s).replace(this.stripTagsRE, "");
10434     },
10435     
10436     /**
10437      * Strips all HTML tags to sort on text only - Case insensitive
10438      * @param {Mixed} s The value being converted
10439      * @return {String} The comparison value
10440      */
10441     asUCText : function(s){
10442         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10443     },
10444     
10445     /**
10446      * Case insensitive string
10447      * @param {Mixed} s The value being converted
10448      * @return {String} The comparison value
10449      */
10450     asUCString : function(s) {
10451         return String(s).toUpperCase();
10452     },
10453     
10454     /**
10455      * Date sorting
10456      * @param {Mixed} s The value being converted
10457      * @return {Number} The comparison value
10458      */
10459     asDate : function(s) {
10460         if(!s){
10461             return 0;
10462         }
10463         if(s instanceof Date){
10464             return s.getTime();
10465         }
10466         return Date.parse(String(s));
10467     },
10468     
10469     /**
10470      * Float sorting
10471      * @param {Mixed} s The value being converted
10472      * @return {Float} The comparison value
10473      */
10474     asFloat : function(s) {
10475         var val = parseFloat(String(s).replace(/,/g, ""));
10476         if(isNaN(val)) {
10477             val = 0;
10478         }
10479         return val;
10480     },
10481     
10482     /**
10483      * Integer sorting
10484      * @param {Mixed} s The value being converted
10485      * @return {Number} The comparison value
10486      */
10487     asInt : function(s) {
10488         var val = parseInt(String(s).replace(/,/g, ""));
10489         if(isNaN(val)) {
10490             val = 0;
10491         }
10492         return val;
10493     }
10494 };/*
10495  * Based on:
10496  * Ext JS Library 1.1.1
10497  * Copyright(c) 2006-2007, Ext JS, LLC.
10498  *
10499  * Originally Released Under LGPL - original licence link has changed is not relivant.
10500  *
10501  * Fork - LGPL
10502  * <script type="text/javascript">
10503  */
10504
10505 /**
10506 * @class Roo.data.Record
10507  * Instances of this class encapsulate both record <em>definition</em> information, and record
10508  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10509  * to access Records cached in an {@link Roo.data.Store} object.<br>
10510  * <p>
10511  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10512  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10513  * objects.<br>
10514  * <p>
10515  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10516  * @constructor
10517  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10518  * {@link #create}. The parameters are the same.
10519  * @param {Array} data An associative Array of data values keyed by the field name.
10520  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10521  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10522  * not specified an integer id is generated.
10523  */
10524 Roo.data.Record = function(data, id){
10525     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10526     this.data = data;
10527 };
10528
10529 /**
10530  * Generate a constructor for a specific record layout.
10531  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10532  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10533  * Each field definition object may contain the following properties: <ul>
10534  * <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,
10535  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10536  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10537  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10538  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10539  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10540  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10541  * this may be omitted.</p></li>
10542  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10543  * <ul><li>auto (Default, implies no conversion)</li>
10544  * <li>string</li>
10545  * <li>int</li>
10546  * <li>float</li>
10547  * <li>boolean</li>
10548  * <li>date</li></ul></p></li>
10549  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10550  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10551  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10552  * by the Reader into an object that will be stored in the Record. It is passed the
10553  * following parameters:<ul>
10554  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10555  * </ul></p></li>
10556  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10557  * </ul>
10558  * <br>usage:<br><pre><code>
10559 var TopicRecord = Roo.data.Record.create(
10560     {name: 'title', mapping: 'topic_title'},
10561     {name: 'author', mapping: 'username'},
10562     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10563     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10564     {name: 'lastPoster', mapping: 'user2'},
10565     {name: 'excerpt', mapping: 'post_text'}
10566 );
10567
10568 var myNewRecord = new TopicRecord({
10569     title: 'Do my job please',
10570     author: 'noobie',
10571     totalPosts: 1,
10572     lastPost: new Date(),
10573     lastPoster: 'Animal',
10574     excerpt: 'No way dude!'
10575 });
10576 myStore.add(myNewRecord);
10577 </code></pre>
10578  * @method create
10579  * @static
10580  */
10581 Roo.data.Record.create = function(o){
10582     var f = function(){
10583         f.superclass.constructor.apply(this, arguments);
10584     };
10585     Roo.extend(f, Roo.data.Record);
10586     var p = f.prototype;
10587     p.fields = new Roo.util.MixedCollection(false, function(field){
10588         return field.name;
10589     });
10590     for(var i = 0, len = o.length; i < len; i++){
10591         p.fields.add(new Roo.data.Field(o[i]));
10592     }
10593     f.getField = function(name){
10594         return p.fields.get(name);  
10595     };
10596     return f;
10597 };
10598
10599 Roo.data.Record.AUTO_ID = 1000;
10600 Roo.data.Record.EDIT = 'edit';
10601 Roo.data.Record.REJECT = 'reject';
10602 Roo.data.Record.COMMIT = 'commit';
10603
10604 Roo.data.Record.prototype = {
10605     /**
10606      * Readonly flag - true if this record has been modified.
10607      * @type Boolean
10608      */
10609     dirty : false,
10610     editing : false,
10611     error: null,
10612     modified: null,
10613
10614     // private
10615     join : function(store){
10616         this.store = store;
10617     },
10618
10619     /**
10620      * Set the named field to the specified value.
10621      * @param {String} name The name of the field to set.
10622      * @param {Object} value The value to set the field to.
10623      */
10624     set : function(name, value){
10625         if(this.data[name] == value){
10626             return;
10627         }
10628         this.dirty = true;
10629         if(!this.modified){
10630             this.modified = {};
10631         }
10632         if(typeof this.modified[name] == 'undefined'){
10633             this.modified[name] = this.data[name];
10634         }
10635         this.data[name] = value;
10636         if(!this.editing && this.store){
10637             this.store.afterEdit(this);
10638         }       
10639     },
10640
10641     /**
10642      * Get the value of the named field.
10643      * @param {String} name The name of the field to get the value of.
10644      * @return {Object} The value of the field.
10645      */
10646     get : function(name){
10647         return this.data[name]; 
10648     },
10649
10650     // private
10651     beginEdit : function(){
10652         this.editing = true;
10653         this.modified = {}; 
10654     },
10655
10656     // private
10657     cancelEdit : function(){
10658         this.editing = false;
10659         delete this.modified;
10660     },
10661
10662     // private
10663     endEdit : function(){
10664         this.editing = false;
10665         if(this.dirty && this.store){
10666             this.store.afterEdit(this);
10667         }
10668     },
10669
10670     /**
10671      * Usually called by the {@link Roo.data.Store} which owns the Record.
10672      * Rejects all changes made to the Record since either creation, or the last commit operation.
10673      * Modified fields are reverted to their original values.
10674      * <p>
10675      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10676      * of reject operations.
10677      */
10678     reject : function(){
10679         var m = this.modified;
10680         for(var n in m){
10681             if(typeof m[n] != "function"){
10682                 this.data[n] = m[n];
10683             }
10684         }
10685         this.dirty = false;
10686         delete this.modified;
10687         this.editing = false;
10688         if(this.store){
10689             this.store.afterReject(this);
10690         }
10691     },
10692
10693     /**
10694      * Usually called by the {@link Roo.data.Store} which owns the Record.
10695      * Commits all changes made to the Record since either creation, or the last commit operation.
10696      * <p>
10697      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10698      * of commit operations.
10699      */
10700     commit : function(){
10701         this.dirty = false;
10702         delete this.modified;
10703         this.editing = false;
10704         if(this.store){
10705             this.store.afterCommit(this);
10706         }
10707     },
10708
10709     // private
10710     hasError : function(){
10711         return this.error != null;
10712     },
10713
10714     // private
10715     clearError : function(){
10716         this.error = null;
10717     },
10718
10719     /**
10720      * Creates a copy of this record.
10721      * @param {String} id (optional) A new record id if you don't want to use this record's id
10722      * @return {Record}
10723      */
10724     copy : function(newId) {
10725         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10726     }
10727 };/*
10728  * Based on:
10729  * Ext JS Library 1.1.1
10730  * Copyright(c) 2006-2007, Ext JS, LLC.
10731  *
10732  * Originally Released Under LGPL - original licence link has changed is not relivant.
10733  *
10734  * Fork - LGPL
10735  * <script type="text/javascript">
10736  */
10737
10738
10739
10740 /**
10741  * @class Roo.data.Store
10742  * @extends Roo.util.Observable
10743  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10744  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10745  * <p>
10746  * 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
10747  * has no knowledge of the format of the data returned by the Proxy.<br>
10748  * <p>
10749  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10750  * instances from the data object. These records are cached and made available through accessor functions.
10751  * @constructor
10752  * Creates a new Store.
10753  * @param {Object} config A config object containing the objects needed for the Store to access data,
10754  * and read the data into Records.
10755  */
10756 Roo.data.Store = function(config){
10757     this.data = new Roo.util.MixedCollection(false);
10758     this.data.getKey = function(o){
10759         return o.id;
10760     };
10761     this.baseParams = {};
10762     // private
10763     this.paramNames = {
10764         "start" : "start",
10765         "limit" : "limit",
10766         "sort" : "sort",
10767         "dir" : "dir",
10768         "multisort" : "_multisort"
10769     };
10770
10771     if(config && config.data){
10772         this.inlineData = config.data;
10773         delete config.data;
10774     }
10775
10776     Roo.apply(this, config);
10777     
10778     if(this.reader){ // reader passed
10779         this.reader = Roo.factory(this.reader, Roo.data);
10780         this.reader.xmodule = this.xmodule || false;
10781         if(!this.recordType){
10782             this.recordType = this.reader.recordType;
10783         }
10784         if(this.reader.onMetaChange){
10785             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10786         }
10787     }
10788
10789     if(this.recordType){
10790         this.fields = this.recordType.prototype.fields;
10791     }
10792     this.modified = [];
10793
10794     this.addEvents({
10795         /**
10796          * @event datachanged
10797          * Fires when the data cache has changed, and a widget which is using this Store
10798          * as a Record cache should refresh its view.
10799          * @param {Store} this
10800          */
10801         datachanged : true,
10802         /**
10803          * @event metachange
10804          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10805          * @param {Store} this
10806          * @param {Object} meta The JSON metadata
10807          */
10808         metachange : true,
10809         /**
10810          * @event add
10811          * Fires when Records have been added to the Store
10812          * @param {Store} this
10813          * @param {Roo.data.Record[]} records The array of Records added
10814          * @param {Number} index The index at which the record(s) were added
10815          */
10816         add : true,
10817         /**
10818          * @event remove
10819          * Fires when a Record has been removed from the Store
10820          * @param {Store} this
10821          * @param {Roo.data.Record} record The Record that was removed
10822          * @param {Number} index The index at which the record was removed
10823          */
10824         remove : true,
10825         /**
10826          * @event update
10827          * Fires when a Record has been updated
10828          * @param {Store} this
10829          * @param {Roo.data.Record} record The Record that was updated
10830          * @param {String} operation The update operation being performed.  Value may be one of:
10831          * <pre><code>
10832  Roo.data.Record.EDIT
10833  Roo.data.Record.REJECT
10834  Roo.data.Record.COMMIT
10835          * </code></pre>
10836          */
10837         update : true,
10838         /**
10839          * @event clear
10840          * Fires when the data cache has been cleared.
10841          * @param {Store} this
10842          */
10843         clear : true,
10844         /**
10845          * @event beforeload
10846          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10847          * the load action will be canceled.
10848          * @param {Store} this
10849          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10850          */
10851         beforeload : true,
10852         /**
10853          * @event beforeloadadd
10854          * Fires after a new set of Records has been loaded.
10855          * @param {Store} this
10856          * @param {Roo.data.Record[]} records The Records that were loaded
10857          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10858          */
10859         beforeloadadd : true,
10860         /**
10861          * @event load
10862          * Fires after a new set of Records has been loaded, before they are added to the store.
10863          * @param {Store} this
10864          * @param {Roo.data.Record[]} records The Records that were loaded
10865          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10866          * @params {Object} return from reader
10867          */
10868         load : true,
10869         /**
10870          * @event loadexception
10871          * Fires if an exception occurs in the Proxy during loading.
10872          * Called with the signature of the Proxy's "loadexception" event.
10873          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10874          * 
10875          * @param {Proxy} 
10876          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10877          * @param {Object} load options 
10878          * @param {Object} jsonData from your request (normally this contains the Exception)
10879          */
10880         loadexception : true
10881     });
10882     
10883     if(this.proxy){
10884         this.proxy = Roo.factory(this.proxy, Roo.data);
10885         this.proxy.xmodule = this.xmodule || false;
10886         this.relayEvents(this.proxy,  ["loadexception"]);
10887     }
10888     this.sortToggle = {};
10889     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10890
10891     Roo.data.Store.superclass.constructor.call(this);
10892
10893     if(this.inlineData){
10894         this.loadData(this.inlineData);
10895         delete this.inlineData;
10896     }
10897 };
10898
10899 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10900      /**
10901     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10902     * without a remote query - used by combo/forms at present.
10903     */
10904     
10905     /**
10906     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10907     */
10908     /**
10909     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10910     */
10911     /**
10912     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10913     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10914     */
10915     /**
10916     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10917     * on any HTTP request
10918     */
10919     /**
10920     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10921     */
10922     /**
10923     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10924     */
10925     multiSort: false,
10926     /**
10927     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10928     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10929     */
10930     remoteSort : false,
10931
10932     /**
10933     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10934      * loaded or when a record is removed. (defaults to false).
10935     */
10936     pruneModifiedRecords : false,
10937
10938     // private
10939     lastOptions : null,
10940
10941     /**
10942      * Add Records to the Store and fires the add event.
10943      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10944      */
10945     add : function(records){
10946         records = [].concat(records);
10947         for(var i = 0, len = records.length; i < len; i++){
10948             records[i].join(this);
10949         }
10950         var index = this.data.length;
10951         this.data.addAll(records);
10952         this.fireEvent("add", this, records, index);
10953     },
10954
10955     /**
10956      * Remove a Record from the Store and fires the remove event.
10957      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10958      */
10959     remove : function(record){
10960         var index = this.data.indexOf(record);
10961         this.data.removeAt(index);
10962         if(this.pruneModifiedRecords){
10963             this.modified.remove(record);
10964         }
10965         this.fireEvent("remove", this, record, index);
10966     },
10967
10968     /**
10969      * Remove all Records from the Store and fires the clear event.
10970      */
10971     removeAll : function(){
10972         this.data.clear();
10973         if(this.pruneModifiedRecords){
10974             this.modified = [];
10975         }
10976         this.fireEvent("clear", this);
10977     },
10978
10979     /**
10980      * Inserts Records to the Store at the given index and fires the add event.
10981      * @param {Number} index The start index at which to insert the passed Records.
10982      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10983      */
10984     insert : function(index, records){
10985         records = [].concat(records);
10986         for(var i = 0, len = records.length; i < len; i++){
10987             this.data.insert(index, records[i]);
10988             records[i].join(this);
10989         }
10990         this.fireEvent("add", this, records, index);
10991     },
10992
10993     /**
10994      * Get the index within the cache of the passed Record.
10995      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10996      * @return {Number} The index of the passed Record. Returns -1 if not found.
10997      */
10998     indexOf : function(record){
10999         return this.data.indexOf(record);
11000     },
11001
11002     /**
11003      * Get the index within the cache of the Record with the passed id.
11004      * @param {String} id The id of the Record to find.
11005      * @return {Number} The index of the Record. Returns -1 if not found.
11006      */
11007     indexOfId : function(id){
11008         return this.data.indexOfKey(id);
11009     },
11010
11011     /**
11012      * Get the Record with the specified id.
11013      * @param {String} id The id of the Record to find.
11014      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11015      */
11016     getById : function(id){
11017         return this.data.key(id);
11018     },
11019
11020     /**
11021      * Get the Record at the specified index.
11022      * @param {Number} index The index of the Record to find.
11023      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11024      */
11025     getAt : function(index){
11026         return this.data.itemAt(index);
11027     },
11028
11029     /**
11030      * Returns a range of Records between specified indices.
11031      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11032      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11033      * @return {Roo.data.Record[]} An array of Records
11034      */
11035     getRange : function(start, end){
11036         return this.data.getRange(start, end);
11037     },
11038
11039     // private
11040     storeOptions : function(o){
11041         o = Roo.apply({}, o);
11042         delete o.callback;
11043         delete o.scope;
11044         this.lastOptions = o;
11045     },
11046
11047     /**
11048      * Loads the Record cache from the configured Proxy using the configured Reader.
11049      * <p>
11050      * If using remote paging, then the first load call must specify the <em>start</em>
11051      * and <em>limit</em> properties in the options.params property to establish the initial
11052      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11053      * <p>
11054      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11055      * and this call will return before the new data has been loaded. Perform any post-processing
11056      * in a callback function, or in a "load" event handler.</strong>
11057      * <p>
11058      * @param {Object} options An object containing properties which control loading options:<ul>
11059      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11060      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11061      * passed the following arguments:<ul>
11062      * <li>r : Roo.data.Record[]</li>
11063      * <li>options: Options object from the load call</li>
11064      * <li>success: Boolean success indicator</li></ul></li>
11065      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11066      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11067      * </ul>
11068      */
11069     load : function(options){
11070         options = options || {};
11071         if(this.fireEvent("beforeload", this, options) !== false){
11072             this.storeOptions(options);
11073             var p = Roo.apply(options.params || {}, this.baseParams);
11074             // if meta was not loaded from remote source.. try requesting it.
11075             if (!this.reader.metaFromRemote) {
11076                 p._requestMeta = 1;
11077             }
11078             if(this.sortInfo && this.remoteSort){
11079                 var pn = this.paramNames;
11080                 p[pn["sort"]] = this.sortInfo.field;
11081                 p[pn["dir"]] = this.sortInfo.direction;
11082             }
11083             if (this.multiSort) {
11084                 var pn = this.paramNames;
11085                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11086             }
11087             
11088             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11089         }
11090     },
11091
11092     /**
11093      * Reloads the Record cache from the configured Proxy using the configured Reader and
11094      * the options from the last load operation performed.
11095      * @param {Object} options (optional) An object containing properties which may override the options
11096      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11097      * the most recently used options are reused).
11098      */
11099     reload : function(options){
11100         this.load(Roo.applyIf(options||{}, this.lastOptions));
11101     },
11102
11103     // private
11104     // Called as a callback by the Reader during a load operation.
11105     loadRecords : function(o, options, success){
11106         if(!o || success === false){
11107             if(success !== false){
11108                 this.fireEvent("load", this, [], options, o);
11109             }
11110             if(options.callback){
11111                 options.callback.call(options.scope || this, [], options, false);
11112             }
11113             return;
11114         }
11115         // if data returned failure - throw an exception.
11116         if (o.success === false) {
11117             // show a message if no listener is registered.
11118             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11119                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11120             }
11121             // loadmask wil be hooked into this..
11122             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11123             return;
11124         }
11125         var r = o.records, t = o.totalRecords || r.length;
11126         
11127         this.fireEvent("beforeloadadd", this, r, options, o);
11128         
11129         if(!options || options.add !== true){
11130             if(this.pruneModifiedRecords){
11131                 this.modified = [];
11132             }
11133             for(var i = 0, len = r.length; i < len; i++){
11134                 r[i].join(this);
11135             }
11136             if(this.snapshot){
11137                 this.data = this.snapshot;
11138                 delete this.snapshot;
11139             }
11140             this.data.clear();
11141             this.data.addAll(r);
11142             this.totalLength = t;
11143             this.applySort();
11144             this.fireEvent("datachanged", this);
11145         }else{
11146             this.totalLength = Math.max(t, this.data.length+r.length);
11147             this.add(r);
11148         }
11149         
11150         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11151                 
11152             var e = new Roo.data.Record({});
11153
11154             e.set(this.parent.displayField, this.parent.emptyTitle);
11155             e.set(this.parent.valueField, '');
11156
11157             this.insert(0, e);
11158         }
11159             
11160         this.fireEvent("load", this, r, options, o);
11161         if(options.callback){
11162             options.callback.call(options.scope || this, r, options, true);
11163         }
11164     },
11165
11166
11167     /**
11168      * Loads data from a passed data block. A Reader which understands the format of the data
11169      * must have been configured in the constructor.
11170      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11171      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11172      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11173      */
11174     loadData : function(o, append){
11175         var r = this.reader.readRecords(o);
11176         this.loadRecords(r, {add: append}, true);
11177     },
11178
11179     /**
11180      * Gets the number of cached records.
11181      * <p>
11182      * <em>If using paging, this may not be the total size of the dataset. If the data object
11183      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11184      * the data set size</em>
11185      */
11186     getCount : function(){
11187         return this.data.length || 0;
11188     },
11189
11190     /**
11191      * Gets the total number of records in the dataset as returned by the server.
11192      * <p>
11193      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11194      * the dataset size</em>
11195      */
11196     getTotalCount : function(){
11197         return this.totalLength || 0;
11198     },
11199
11200     /**
11201      * Returns the sort state of the Store as an object with two properties:
11202      * <pre><code>
11203  field {String} The name of the field by which the Records are sorted
11204  direction {String} The sort order, "ASC" or "DESC"
11205      * </code></pre>
11206      */
11207     getSortState : function(){
11208         return this.sortInfo;
11209     },
11210
11211     // private
11212     applySort : function(){
11213         if(this.sortInfo && !this.remoteSort){
11214             var s = this.sortInfo, f = s.field;
11215             var st = this.fields.get(f).sortType;
11216             var fn = function(r1, r2){
11217                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11218                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11219             };
11220             this.data.sort(s.direction, fn);
11221             if(this.snapshot && this.snapshot != this.data){
11222                 this.snapshot.sort(s.direction, fn);
11223             }
11224         }
11225     },
11226
11227     /**
11228      * Sets the default sort column and order to be used by the next load operation.
11229      * @param {String} fieldName The name of the field to sort by.
11230      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11231      */
11232     setDefaultSort : function(field, dir){
11233         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11234     },
11235
11236     /**
11237      * Sort the Records.
11238      * If remote sorting is used, the sort is performed on the server, and the cache is
11239      * reloaded. If local sorting is used, the cache is sorted internally.
11240      * @param {String} fieldName The name of the field to sort by.
11241      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11242      */
11243     sort : function(fieldName, dir){
11244         var f = this.fields.get(fieldName);
11245         if(!dir){
11246             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11247             
11248             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11249                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11250             }else{
11251                 dir = f.sortDir;
11252             }
11253         }
11254         this.sortToggle[f.name] = dir;
11255         this.sortInfo = {field: f.name, direction: dir};
11256         if(!this.remoteSort){
11257             this.applySort();
11258             this.fireEvent("datachanged", this);
11259         }else{
11260             this.load(this.lastOptions);
11261         }
11262     },
11263
11264     /**
11265      * Calls the specified function for each of the Records in the cache.
11266      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11267      * Returning <em>false</em> aborts and exits the iteration.
11268      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11269      */
11270     each : function(fn, scope){
11271         this.data.each(fn, scope);
11272     },
11273
11274     /**
11275      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11276      * (e.g., during paging).
11277      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11278      */
11279     getModifiedRecords : function(){
11280         return this.modified;
11281     },
11282
11283     // private
11284     createFilterFn : function(property, value, anyMatch){
11285         if(!value.exec){ // not a regex
11286             value = String(value);
11287             if(value.length == 0){
11288                 return false;
11289             }
11290             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11291         }
11292         return function(r){
11293             return value.test(r.data[property]);
11294         };
11295     },
11296
11297     /**
11298      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11299      * @param {String} property A field on your records
11300      * @param {Number} start The record index to start at (defaults to 0)
11301      * @param {Number} end The last record index to include (defaults to length - 1)
11302      * @return {Number} The sum
11303      */
11304     sum : function(property, start, end){
11305         var rs = this.data.items, v = 0;
11306         start = start || 0;
11307         end = (end || end === 0) ? end : rs.length-1;
11308
11309         for(var i = start; i <= end; i++){
11310             v += (rs[i].data[property] || 0);
11311         }
11312         return v;
11313     },
11314
11315     /**
11316      * Filter the records by a specified property.
11317      * @param {String} field A field on your records
11318      * @param {String/RegExp} value Either a string that the field
11319      * should start with or a RegExp to test against the field
11320      * @param {Boolean} anyMatch True to match any part not just the beginning
11321      */
11322     filter : function(property, value, anyMatch){
11323         var fn = this.createFilterFn(property, value, anyMatch);
11324         return fn ? this.filterBy(fn) : this.clearFilter();
11325     },
11326
11327     /**
11328      * Filter by a function. The specified function will be called with each
11329      * record in this data source. If the function returns true the record is included,
11330      * otherwise it is filtered.
11331      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11332      * @param {Object} scope (optional) The scope of the function (defaults to this)
11333      */
11334     filterBy : function(fn, scope){
11335         this.snapshot = this.snapshot || this.data;
11336         this.data = this.queryBy(fn, scope||this);
11337         this.fireEvent("datachanged", this);
11338     },
11339
11340     /**
11341      * Query the records by a specified property.
11342      * @param {String} field A field on your records
11343      * @param {String/RegExp} value Either a string that the field
11344      * should start with or a RegExp to test against the field
11345      * @param {Boolean} anyMatch True to match any part not just the beginning
11346      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11347      */
11348     query : function(property, value, anyMatch){
11349         var fn = this.createFilterFn(property, value, anyMatch);
11350         return fn ? this.queryBy(fn) : this.data.clone();
11351     },
11352
11353     /**
11354      * Query by a function. The specified function will be called with each
11355      * record in this data source. If the function returns true the record is included
11356      * in the results.
11357      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11358      * @param {Object} scope (optional) The scope of the function (defaults to this)
11359       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11360      **/
11361     queryBy : function(fn, scope){
11362         var data = this.snapshot || this.data;
11363         return data.filterBy(fn, scope||this);
11364     },
11365
11366     /**
11367      * Collects unique values for a particular dataIndex from this store.
11368      * @param {String} dataIndex The property to collect
11369      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11370      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11371      * @return {Array} An array of the unique values
11372      **/
11373     collect : function(dataIndex, allowNull, bypassFilter){
11374         var d = (bypassFilter === true && this.snapshot) ?
11375                 this.snapshot.items : this.data.items;
11376         var v, sv, r = [], l = {};
11377         for(var i = 0, len = d.length; i < len; i++){
11378             v = d[i].data[dataIndex];
11379             sv = String(v);
11380             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11381                 l[sv] = true;
11382                 r[r.length] = v;
11383             }
11384         }
11385         return r;
11386     },
11387
11388     /**
11389      * Revert to a view of the Record cache with no filtering applied.
11390      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11391      */
11392     clearFilter : function(suppressEvent){
11393         if(this.snapshot && this.snapshot != this.data){
11394             this.data = this.snapshot;
11395             delete this.snapshot;
11396             if(suppressEvent !== true){
11397                 this.fireEvent("datachanged", this);
11398             }
11399         }
11400     },
11401
11402     // private
11403     afterEdit : function(record){
11404         if(this.modified.indexOf(record) == -1){
11405             this.modified.push(record);
11406         }
11407         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11408     },
11409     
11410     // private
11411     afterReject : function(record){
11412         this.modified.remove(record);
11413         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11414     },
11415
11416     // private
11417     afterCommit : function(record){
11418         this.modified.remove(record);
11419         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11420     },
11421
11422     /**
11423      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11424      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11425      */
11426     commitChanges : function(){
11427         var m = this.modified.slice(0);
11428         this.modified = [];
11429         for(var i = 0, len = m.length; i < len; i++){
11430             m[i].commit();
11431         }
11432     },
11433
11434     /**
11435      * Cancel outstanding changes on all changed records.
11436      */
11437     rejectChanges : function(){
11438         var m = this.modified.slice(0);
11439         this.modified = [];
11440         for(var i = 0, len = m.length; i < len; i++){
11441             m[i].reject();
11442         }
11443     },
11444
11445     onMetaChange : function(meta, rtype, o){
11446         this.recordType = rtype;
11447         this.fields = rtype.prototype.fields;
11448         delete this.snapshot;
11449         this.sortInfo = meta.sortInfo || this.sortInfo;
11450         this.modified = [];
11451         this.fireEvent('metachange', this, this.reader.meta);
11452     },
11453     
11454     moveIndex : function(data, type)
11455     {
11456         var index = this.indexOf(data);
11457         
11458         var newIndex = index + type;
11459         
11460         this.remove(data);
11461         
11462         this.insert(newIndex, data);
11463         
11464     }
11465 });/*
11466  * Based on:
11467  * Ext JS Library 1.1.1
11468  * Copyright(c) 2006-2007, Ext JS, LLC.
11469  *
11470  * Originally Released Under LGPL - original licence link has changed is not relivant.
11471  *
11472  * Fork - LGPL
11473  * <script type="text/javascript">
11474  */
11475
11476 /**
11477  * @class Roo.data.SimpleStore
11478  * @extends Roo.data.Store
11479  * Small helper class to make creating Stores from Array data easier.
11480  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11481  * @cfg {Array} fields An array of field definition objects, or field name strings.
11482  * @cfg {Array} data The multi-dimensional array of data
11483  * @constructor
11484  * @param {Object} config
11485  */
11486 Roo.data.SimpleStore = function(config){
11487     Roo.data.SimpleStore.superclass.constructor.call(this, {
11488         isLocal : true,
11489         reader: new Roo.data.ArrayReader({
11490                 id: config.id
11491             },
11492             Roo.data.Record.create(config.fields)
11493         ),
11494         proxy : new Roo.data.MemoryProxy(config.data)
11495     });
11496     this.load();
11497 };
11498 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11499  * Based on:
11500  * Ext JS Library 1.1.1
11501  * Copyright(c) 2006-2007, Ext JS, LLC.
11502  *
11503  * Originally Released Under LGPL - original licence link has changed is not relivant.
11504  *
11505  * Fork - LGPL
11506  * <script type="text/javascript">
11507  */
11508
11509 /**
11510 /**
11511  * @extends Roo.data.Store
11512  * @class Roo.data.JsonStore
11513  * Small helper class to make creating Stores for JSON data easier. <br/>
11514 <pre><code>
11515 var store = new Roo.data.JsonStore({
11516     url: 'get-images.php',
11517     root: 'images',
11518     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11519 });
11520 </code></pre>
11521  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11522  * JsonReader and HttpProxy (unless inline data is provided).</b>
11523  * @cfg {Array} fields An array of field definition objects, or field name strings.
11524  * @constructor
11525  * @param {Object} config
11526  */
11527 Roo.data.JsonStore = function(c){
11528     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11529         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11530         reader: new Roo.data.JsonReader(c, c.fields)
11531     }));
11532 };
11533 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11534  * Based on:
11535  * Ext JS Library 1.1.1
11536  * Copyright(c) 2006-2007, Ext JS, LLC.
11537  *
11538  * Originally Released Under LGPL - original licence link has changed is not relivant.
11539  *
11540  * Fork - LGPL
11541  * <script type="text/javascript">
11542  */
11543
11544  
11545 Roo.data.Field = function(config){
11546     if(typeof config == "string"){
11547         config = {name: config};
11548     }
11549     Roo.apply(this, config);
11550     
11551     if(!this.type){
11552         this.type = "auto";
11553     }
11554     
11555     var st = Roo.data.SortTypes;
11556     // named sortTypes are supported, here we look them up
11557     if(typeof this.sortType == "string"){
11558         this.sortType = st[this.sortType];
11559     }
11560     
11561     // set default sortType for strings and dates
11562     if(!this.sortType){
11563         switch(this.type){
11564             case "string":
11565                 this.sortType = st.asUCString;
11566                 break;
11567             case "date":
11568                 this.sortType = st.asDate;
11569                 break;
11570             default:
11571                 this.sortType = st.none;
11572         }
11573     }
11574
11575     // define once
11576     var stripRe = /[\$,%]/g;
11577
11578     // prebuilt conversion function for this field, instead of
11579     // switching every time we're reading a value
11580     if(!this.convert){
11581         var cv, dateFormat = this.dateFormat;
11582         switch(this.type){
11583             case "":
11584             case "auto":
11585             case undefined:
11586                 cv = function(v){ return v; };
11587                 break;
11588             case "string":
11589                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11590                 break;
11591             case "int":
11592                 cv = function(v){
11593                     return v !== undefined && v !== null && v !== '' ?
11594                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11595                     };
11596                 break;
11597             case "float":
11598                 cv = function(v){
11599                     return v !== undefined && v !== null && v !== '' ?
11600                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11601                     };
11602                 break;
11603             case "bool":
11604             case "boolean":
11605                 cv = function(v){ return v === true || v === "true" || v == 1; };
11606                 break;
11607             case "date":
11608                 cv = function(v){
11609                     if(!v){
11610                         return '';
11611                     }
11612                     if(v instanceof Date){
11613                         return v;
11614                     }
11615                     if(dateFormat){
11616                         if(dateFormat == "timestamp"){
11617                             return new Date(v*1000);
11618                         }
11619                         return Date.parseDate(v, dateFormat);
11620                     }
11621                     var parsed = Date.parse(v);
11622                     return parsed ? new Date(parsed) : null;
11623                 };
11624              break;
11625             
11626         }
11627         this.convert = cv;
11628     }
11629 };
11630
11631 Roo.data.Field.prototype = {
11632     dateFormat: null,
11633     defaultValue: "",
11634     mapping: null,
11635     sortType : null,
11636     sortDir : "ASC"
11637 };/*
11638  * Based on:
11639  * Ext JS Library 1.1.1
11640  * Copyright(c) 2006-2007, Ext JS, LLC.
11641  *
11642  * Originally Released Under LGPL - original licence link has changed is not relivant.
11643  *
11644  * Fork - LGPL
11645  * <script type="text/javascript">
11646  */
11647  
11648 // Base class for reading structured data from a data source.  This class is intended to be
11649 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11650
11651 /**
11652  * @class Roo.data.DataReader
11653  * Base class for reading structured data from a data source.  This class is intended to be
11654  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11655  */
11656
11657 Roo.data.DataReader = function(meta, recordType){
11658     
11659     this.meta = meta;
11660     
11661     this.recordType = recordType instanceof Array ? 
11662         Roo.data.Record.create(recordType) : recordType;
11663 };
11664
11665 Roo.data.DataReader.prototype = {
11666      /**
11667      * Create an empty record
11668      * @param {Object} data (optional) - overlay some values
11669      * @return {Roo.data.Record} record created.
11670      */
11671     newRow :  function(d) {
11672         var da =  {};
11673         this.recordType.prototype.fields.each(function(c) {
11674             switch( c.type) {
11675                 case 'int' : da[c.name] = 0; break;
11676                 case 'date' : da[c.name] = new Date(); break;
11677                 case 'float' : da[c.name] = 0.0; break;
11678                 case 'boolean' : da[c.name] = false; break;
11679                 default : da[c.name] = ""; break;
11680             }
11681             
11682         });
11683         return new this.recordType(Roo.apply(da, d));
11684     }
11685     
11686 };/*
11687  * Based on:
11688  * Ext JS Library 1.1.1
11689  * Copyright(c) 2006-2007, Ext JS, LLC.
11690  *
11691  * Originally Released Under LGPL - original licence link has changed is not relivant.
11692  *
11693  * Fork - LGPL
11694  * <script type="text/javascript">
11695  */
11696
11697 /**
11698  * @class Roo.data.DataProxy
11699  * @extends Roo.data.Observable
11700  * This class is an abstract base class for implementations which provide retrieval of
11701  * unformatted data objects.<br>
11702  * <p>
11703  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11704  * (of the appropriate type which knows how to parse the data object) to provide a block of
11705  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11706  * <p>
11707  * Custom implementations must implement the load method as described in
11708  * {@link Roo.data.HttpProxy#load}.
11709  */
11710 Roo.data.DataProxy = function(){
11711     this.addEvents({
11712         /**
11713          * @event beforeload
11714          * Fires before a network request is made to retrieve a data object.
11715          * @param {Object} This DataProxy object.
11716          * @param {Object} params The params parameter to the load function.
11717          */
11718         beforeload : true,
11719         /**
11720          * @event load
11721          * Fires before the load method's callback is called.
11722          * @param {Object} This DataProxy object.
11723          * @param {Object} o The data object.
11724          * @param {Object} arg The callback argument object passed to the load function.
11725          */
11726         load : true,
11727         /**
11728          * @event loadexception
11729          * Fires if an Exception occurs during data retrieval.
11730          * @param {Object} This DataProxy object.
11731          * @param {Object} o The data object.
11732          * @param {Object} arg The callback argument object passed to the load function.
11733          * @param {Object} e The Exception.
11734          */
11735         loadexception : true
11736     });
11737     Roo.data.DataProxy.superclass.constructor.call(this);
11738 };
11739
11740 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11741
11742     /**
11743      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11744      */
11745 /*
11746  * Based on:
11747  * Ext JS Library 1.1.1
11748  * Copyright(c) 2006-2007, Ext JS, LLC.
11749  *
11750  * Originally Released Under LGPL - original licence link has changed is not relivant.
11751  *
11752  * Fork - LGPL
11753  * <script type="text/javascript">
11754  */
11755 /**
11756  * @class Roo.data.MemoryProxy
11757  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11758  * to the Reader when its load method is called.
11759  * @constructor
11760  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11761  */
11762 Roo.data.MemoryProxy = function(data){
11763     if (data.data) {
11764         data = data.data;
11765     }
11766     Roo.data.MemoryProxy.superclass.constructor.call(this);
11767     this.data = data;
11768 };
11769
11770 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11771     
11772     /**
11773      * Load data from the requested source (in this case an in-memory
11774      * data object passed to the constructor), read the data object into
11775      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11776      * process that block using the passed callback.
11777      * @param {Object} params This parameter is not used by the MemoryProxy class.
11778      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11779      * object into a block of Roo.data.Records.
11780      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11781      * The function must be passed <ul>
11782      * <li>The Record block object</li>
11783      * <li>The "arg" argument from the load function</li>
11784      * <li>A boolean success indicator</li>
11785      * </ul>
11786      * @param {Object} scope The scope in which to call the callback
11787      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11788      */
11789     load : function(params, reader, callback, scope, arg){
11790         params = params || {};
11791         var result;
11792         try {
11793             result = reader.readRecords(this.data);
11794         }catch(e){
11795             this.fireEvent("loadexception", this, arg, null, e);
11796             callback.call(scope, null, arg, false);
11797             return;
11798         }
11799         callback.call(scope, result, arg, true);
11800     },
11801     
11802     // private
11803     update : function(params, records){
11804         
11805     }
11806 });/*
11807  * Based on:
11808  * Ext JS Library 1.1.1
11809  * Copyright(c) 2006-2007, Ext JS, LLC.
11810  *
11811  * Originally Released Under LGPL - original licence link has changed is not relivant.
11812  *
11813  * Fork - LGPL
11814  * <script type="text/javascript">
11815  */
11816 /**
11817  * @class Roo.data.HttpProxy
11818  * @extends Roo.data.DataProxy
11819  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11820  * configured to reference a certain URL.<br><br>
11821  * <p>
11822  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11823  * from which the running page was served.<br><br>
11824  * <p>
11825  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11826  * <p>
11827  * Be aware that to enable the browser to parse an XML document, the server must set
11828  * the Content-Type header in the HTTP response to "text/xml".
11829  * @constructor
11830  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11831  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11832  * will be used to make the request.
11833  */
11834 Roo.data.HttpProxy = function(conn){
11835     Roo.data.HttpProxy.superclass.constructor.call(this);
11836     // is conn a conn config or a real conn?
11837     this.conn = conn;
11838     this.useAjax = !conn || !conn.events;
11839   
11840 };
11841
11842 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11843     // thse are take from connection...
11844     
11845     /**
11846      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11847      */
11848     /**
11849      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11850      * extra parameters to each request made by this object. (defaults to undefined)
11851      */
11852     /**
11853      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11854      *  to each request made by this object. (defaults to undefined)
11855      */
11856     /**
11857      * @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)
11858      */
11859     /**
11860      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11861      */
11862      /**
11863      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11864      * @type Boolean
11865      */
11866   
11867
11868     /**
11869      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11870      * @type Boolean
11871      */
11872     /**
11873      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11874      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11875      * a finer-grained basis than the DataProxy events.
11876      */
11877     getConnection : function(){
11878         return this.useAjax ? Roo.Ajax : this.conn;
11879     },
11880
11881     /**
11882      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11883      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11884      * process that block using the passed callback.
11885      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11886      * for the request to the remote server.
11887      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11888      * object into a block of Roo.data.Records.
11889      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11890      * The function must be passed <ul>
11891      * <li>The Record block object</li>
11892      * <li>The "arg" argument from the load function</li>
11893      * <li>A boolean success indicator</li>
11894      * </ul>
11895      * @param {Object} scope The scope in which to call the callback
11896      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11897      */
11898     load : function(params, reader, callback, scope, arg){
11899         if(this.fireEvent("beforeload", this, params) !== false){
11900             var  o = {
11901                 params : params || {},
11902                 request: {
11903                     callback : callback,
11904                     scope : scope,
11905                     arg : arg
11906                 },
11907                 reader: reader,
11908                 callback : this.loadResponse,
11909                 scope: this
11910             };
11911             if(this.useAjax){
11912                 Roo.applyIf(o, this.conn);
11913                 if(this.activeRequest){
11914                     Roo.Ajax.abort(this.activeRequest);
11915                 }
11916                 this.activeRequest = Roo.Ajax.request(o);
11917             }else{
11918                 this.conn.request(o);
11919             }
11920         }else{
11921             callback.call(scope||this, null, arg, false);
11922         }
11923     },
11924
11925     // private
11926     loadResponse : function(o, success, response){
11927         delete this.activeRequest;
11928         if(!success){
11929             this.fireEvent("loadexception", this, o, response);
11930             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11931             return;
11932         }
11933         var result;
11934         try {
11935             result = o.reader.read(response);
11936         }catch(e){
11937             this.fireEvent("loadexception", this, o, response, e);
11938             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11939             return;
11940         }
11941         
11942         this.fireEvent("load", this, o, o.request.arg);
11943         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11944     },
11945
11946     // private
11947     update : function(dataSet){
11948
11949     },
11950
11951     // private
11952     updateResponse : function(dataSet){
11953
11954     }
11955 });/*
11956  * Based on:
11957  * Ext JS Library 1.1.1
11958  * Copyright(c) 2006-2007, Ext JS, LLC.
11959  *
11960  * Originally Released Under LGPL - original licence link has changed is not relivant.
11961  *
11962  * Fork - LGPL
11963  * <script type="text/javascript">
11964  */
11965
11966 /**
11967  * @class Roo.data.ScriptTagProxy
11968  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11969  * other than the originating domain of the running page.<br><br>
11970  * <p>
11971  * <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
11972  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11973  * <p>
11974  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11975  * source code that is used as the source inside a &lt;script> tag.<br><br>
11976  * <p>
11977  * In order for the browser to process the returned data, the server must wrap the data object
11978  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11979  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11980  * depending on whether the callback name was passed:
11981  * <p>
11982  * <pre><code>
11983 boolean scriptTag = false;
11984 String cb = request.getParameter("callback");
11985 if (cb != null) {
11986     scriptTag = true;
11987     response.setContentType("text/javascript");
11988 } else {
11989     response.setContentType("application/x-json");
11990 }
11991 Writer out = response.getWriter();
11992 if (scriptTag) {
11993     out.write(cb + "(");
11994 }
11995 out.print(dataBlock.toJsonString());
11996 if (scriptTag) {
11997     out.write(");");
11998 }
11999 </pre></code>
12000  *
12001  * @constructor
12002  * @param {Object} config A configuration object.
12003  */
12004 Roo.data.ScriptTagProxy = function(config){
12005     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12006     Roo.apply(this, config);
12007     this.head = document.getElementsByTagName("head")[0];
12008 };
12009
12010 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12011
12012 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12013     /**
12014      * @cfg {String} url The URL from which to request the data object.
12015      */
12016     /**
12017      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12018      */
12019     timeout : 30000,
12020     /**
12021      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12022      * the server the name of the callback function set up by the load call to process the returned data object.
12023      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12024      * javascript output which calls this named function passing the data object as its only parameter.
12025      */
12026     callbackParam : "callback",
12027     /**
12028      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12029      * name to the request.
12030      */
12031     nocache : true,
12032
12033     /**
12034      * Load data from the configured URL, read the data object into
12035      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12036      * process that block using the passed callback.
12037      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12038      * for the request to the remote server.
12039      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12040      * object into a block of Roo.data.Records.
12041      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12042      * The function must be passed <ul>
12043      * <li>The Record block object</li>
12044      * <li>The "arg" argument from the load function</li>
12045      * <li>A boolean success indicator</li>
12046      * </ul>
12047      * @param {Object} scope The scope in which to call the callback
12048      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12049      */
12050     load : function(params, reader, callback, scope, arg){
12051         if(this.fireEvent("beforeload", this, params) !== false){
12052
12053             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12054
12055             var url = this.url;
12056             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12057             if(this.nocache){
12058                 url += "&_dc=" + (new Date().getTime());
12059             }
12060             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12061             var trans = {
12062                 id : transId,
12063                 cb : "stcCallback"+transId,
12064                 scriptId : "stcScript"+transId,
12065                 params : params,
12066                 arg : arg,
12067                 url : url,
12068                 callback : callback,
12069                 scope : scope,
12070                 reader : reader
12071             };
12072             var conn = this;
12073
12074             window[trans.cb] = function(o){
12075                 conn.handleResponse(o, trans);
12076             };
12077
12078             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12079
12080             if(this.autoAbort !== false){
12081                 this.abort();
12082             }
12083
12084             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12085
12086             var script = document.createElement("script");
12087             script.setAttribute("src", url);
12088             script.setAttribute("type", "text/javascript");
12089             script.setAttribute("id", trans.scriptId);
12090             this.head.appendChild(script);
12091
12092             this.trans = trans;
12093         }else{
12094             callback.call(scope||this, null, arg, false);
12095         }
12096     },
12097
12098     // private
12099     isLoading : function(){
12100         return this.trans ? true : false;
12101     },
12102
12103     /**
12104      * Abort the current server request.
12105      */
12106     abort : function(){
12107         if(this.isLoading()){
12108             this.destroyTrans(this.trans);
12109         }
12110     },
12111
12112     // private
12113     destroyTrans : function(trans, isLoaded){
12114         this.head.removeChild(document.getElementById(trans.scriptId));
12115         clearTimeout(trans.timeoutId);
12116         if(isLoaded){
12117             window[trans.cb] = undefined;
12118             try{
12119                 delete window[trans.cb];
12120             }catch(e){}
12121         }else{
12122             // if hasn't been loaded, wait for load to remove it to prevent script error
12123             window[trans.cb] = function(){
12124                 window[trans.cb] = undefined;
12125                 try{
12126                     delete window[trans.cb];
12127                 }catch(e){}
12128             };
12129         }
12130     },
12131
12132     // private
12133     handleResponse : function(o, trans){
12134         this.trans = false;
12135         this.destroyTrans(trans, true);
12136         var result;
12137         try {
12138             result = trans.reader.readRecords(o);
12139         }catch(e){
12140             this.fireEvent("loadexception", this, o, trans.arg, e);
12141             trans.callback.call(trans.scope||window, null, trans.arg, false);
12142             return;
12143         }
12144         this.fireEvent("load", this, o, trans.arg);
12145         trans.callback.call(trans.scope||window, result, trans.arg, true);
12146     },
12147
12148     // private
12149     handleFailure : function(trans){
12150         this.trans = false;
12151         this.destroyTrans(trans, false);
12152         this.fireEvent("loadexception", this, null, trans.arg);
12153         trans.callback.call(trans.scope||window, null, trans.arg, false);
12154     }
12155 });/*
12156  * Based on:
12157  * Ext JS Library 1.1.1
12158  * Copyright(c) 2006-2007, Ext JS, LLC.
12159  *
12160  * Originally Released Under LGPL - original licence link has changed is not relivant.
12161  *
12162  * Fork - LGPL
12163  * <script type="text/javascript">
12164  */
12165
12166 /**
12167  * @class Roo.data.JsonReader
12168  * @extends Roo.data.DataReader
12169  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12170  * based on mappings in a provided Roo.data.Record constructor.
12171  * 
12172  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12173  * in the reply previously. 
12174  * 
12175  * <p>
12176  * Example code:
12177  * <pre><code>
12178 var RecordDef = Roo.data.Record.create([
12179     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12180     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12181 ]);
12182 var myReader = new Roo.data.JsonReader({
12183     totalProperty: "results",    // The property which contains the total dataset size (optional)
12184     root: "rows",                // The property which contains an Array of row objects
12185     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12186 }, RecordDef);
12187 </code></pre>
12188  * <p>
12189  * This would consume a JSON file like this:
12190  * <pre><code>
12191 { 'results': 2, 'rows': [
12192     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12193     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12194 }
12195 </code></pre>
12196  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12197  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12198  * paged from the remote server.
12199  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12200  * @cfg {String} root name of the property which contains the Array of row objects.
12201  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12202  * @cfg {Array} fields Array of field definition objects
12203  * @constructor
12204  * Create a new JsonReader
12205  * @param {Object} meta Metadata configuration options
12206  * @param {Object} recordType Either an Array of field definition objects,
12207  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12208  */
12209 Roo.data.JsonReader = function(meta, recordType){
12210     
12211     meta = meta || {};
12212     // set some defaults:
12213     Roo.applyIf(meta, {
12214         totalProperty: 'total',
12215         successProperty : 'success',
12216         root : 'data',
12217         id : 'id'
12218     });
12219     
12220     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12221 };
12222 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12223     
12224     /**
12225      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12226      * Used by Store query builder to append _requestMeta to params.
12227      * 
12228      */
12229     metaFromRemote : false,
12230     /**
12231      * This method is only used by a DataProxy which has retrieved data from a remote server.
12232      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12233      * @return {Object} data A data block which is used by an Roo.data.Store object as
12234      * a cache of Roo.data.Records.
12235      */
12236     read : function(response){
12237         var json = response.responseText;
12238        
12239         var o = /* eval:var:o */ eval("("+json+")");
12240         if(!o) {
12241             throw {message: "JsonReader.read: Json object not found"};
12242         }
12243         
12244         if(o.metaData){
12245             
12246             delete this.ef;
12247             this.metaFromRemote = true;
12248             this.meta = o.metaData;
12249             this.recordType = Roo.data.Record.create(o.metaData.fields);
12250             this.onMetaChange(this.meta, this.recordType, o);
12251         }
12252         return this.readRecords(o);
12253     },
12254
12255     // private function a store will implement
12256     onMetaChange : function(meta, recordType, o){
12257
12258     },
12259
12260     /**
12261          * @ignore
12262          */
12263     simpleAccess: function(obj, subsc) {
12264         return obj[subsc];
12265     },
12266
12267         /**
12268          * @ignore
12269          */
12270     getJsonAccessor: function(){
12271         var re = /[\[\.]/;
12272         return function(expr) {
12273             try {
12274                 return(re.test(expr))
12275                     ? new Function("obj", "return obj." + expr)
12276                     : function(obj){
12277                         return obj[expr];
12278                     };
12279             } catch(e){}
12280             return Roo.emptyFn;
12281         };
12282     }(),
12283
12284     /**
12285      * Create a data block containing Roo.data.Records from an XML document.
12286      * @param {Object} o An object which contains an Array of row objects in the property specified
12287      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12288      * which contains the total size of the dataset.
12289      * @return {Object} data A data block which is used by an Roo.data.Store object as
12290      * a cache of Roo.data.Records.
12291      */
12292     readRecords : function(o){
12293         /**
12294          * After any data loads, the raw JSON data is available for further custom processing.
12295          * @type Object
12296          */
12297         this.o = o;
12298         var s = this.meta, Record = this.recordType,
12299             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12300
12301 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12302         if (!this.ef) {
12303             if(s.totalProperty) {
12304                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12305                 }
12306                 if(s.successProperty) {
12307                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12308                 }
12309                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12310                 if (s.id) {
12311                         var g = this.getJsonAccessor(s.id);
12312                         this.getId = function(rec) {
12313                                 var r = g(rec);  
12314                                 return (r === undefined || r === "") ? null : r;
12315                         };
12316                 } else {
12317                         this.getId = function(){return null;};
12318                 }
12319             this.ef = [];
12320             for(var jj = 0; jj < fl; jj++){
12321                 f = fi[jj];
12322                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12323                 this.ef[jj] = this.getJsonAccessor(map);
12324             }
12325         }
12326
12327         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12328         if(s.totalProperty){
12329             var vt = parseInt(this.getTotal(o), 10);
12330             if(!isNaN(vt)){
12331                 totalRecords = vt;
12332             }
12333         }
12334         if(s.successProperty){
12335             var vs = this.getSuccess(o);
12336             if(vs === false || vs === 'false'){
12337                 success = false;
12338             }
12339         }
12340         var records = [];
12341         for(var i = 0; i < c; i++){
12342                 var n = root[i];
12343             var values = {};
12344             var id = this.getId(n);
12345             for(var j = 0; j < fl; j++){
12346                 f = fi[j];
12347             var v = this.ef[j](n);
12348             if (!f.convert) {
12349                 Roo.log('missing convert for ' + f.name);
12350                 Roo.log(f);
12351                 continue;
12352             }
12353             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12354             }
12355             var record = new Record(values, id);
12356             record.json = n;
12357             records[i] = record;
12358         }
12359         return {
12360             raw : o,
12361             success : success,
12362             records : records,
12363             totalRecords : totalRecords
12364         };
12365     }
12366 });/*
12367  * Based on:
12368  * Ext JS Library 1.1.1
12369  * Copyright(c) 2006-2007, Ext JS, LLC.
12370  *
12371  * Originally Released Under LGPL - original licence link has changed is not relivant.
12372  *
12373  * Fork - LGPL
12374  * <script type="text/javascript">
12375  */
12376
12377 /**
12378  * @class Roo.data.ArrayReader
12379  * @extends Roo.data.DataReader
12380  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12381  * Each element of that Array represents a row of data fields. The
12382  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12383  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12384  * <p>
12385  * Example code:.
12386  * <pre><code>
12387 var RecordDef = Roo.data.Record.create([
12388     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12389     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12390 ]);
12391 var myReader = new Roo.data.ArrayReader({
12392     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12393 }, RecordDef);
12394 </code></pre>
12395  * <p>
12396  * This would consume an Array like this:
12397  * <pre><code>
12398 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12399   </code></pre>
12400  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12401  * @constructor
12402  * Create a new JsonReader
12403  * @param {Object} meta Metadata configuration options.
12404  * @param {Object} recordType Either an Array of field definition objects
12405  * as specified to {@link Roo.data.Record#create},
12406  * or an {@link Roo.data.Record} object
12407  * created using {@link Roo.data.Record#create}.
12408  */
12409 Roo.data.ArrayReader = function(meta, recordType){
12410     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12411 };
12412
12413 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12414     /**
12415      * Create a data block containing Roo.data.Records from an XML document.
12416      * @param {Object} o An Array of row objects which represents the dataset.
12417      * @return {Object} data A data block which is used by an Roo.data.Store object as
12418      * a cache of Roo.data.Records.
12419      */
12420     readRecords : function(o){
12421         var sid = this.meta ? this.meta.id : null;
12422         var recordType = this.recordType, fields = recordType.prototype.fields;
12423         var records = [];
12424         var root = o;
12425             for(var i = 0; i < root.length; i++){
12426                     var n = root[i];
12427                 var values = {};
12428                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12429                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12430                 var f = fields.items[j];
12431                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12432                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12433                 v = f.convert(v);
12434                 values[f.name] = v;
12435             }
12436                 var record = new recordType(values, id);
12437                 record.json = n;
12438                 records[records.length] = record;
12439             }
12440             return {
12441                 records : records,
12442                 totalRecords : records.length
12443             };
12444     }
12445 });/*
12446  * - LGPL
12447  * * 
12448  */
12449
12450 /**
12451  * @class Roo.bootstrap.ComboBox
12452  * @extends Roo.bootstrap.TriggerField
12453  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12454  * @cfg {Boolean} append (true|false) default false
12455  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12456  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12457  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12458  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12459  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12460  * @cfg {Boolean} animate default true
12461  * @cfg {Boolean} emptyResultText only for touch device
12462  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12463  * @cfg {String} emptyTitle default ''
12464  * @constructor
12465  * Create a new ComboBox.
12466  * @param {Object} config Configuration options
12467  */
12468 Roo.bootstrap.ComboBox = function(config){
12469     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12470     this.addEvents({
12471         /**
12472          * @event expand
12473          * Fires when the dropdown list is expanded
12474         * @param {Roo.bootstrap.ComboBox} combo This combo box
12475         */
12476         'expand' : true,
12477         /**
12478          * @event collapse
12479          * Fires when the dropdown list is collapsed
12480         * @param {Roo.bootstrap.ComboBox} combo This combo box
12481         */
12482         'collapse' : true,
12483         /**
12484          * @event beforeselect
12485          * Fires before a list item is selected. Return false to cancel the selection.
12486         * @param {Roo.bootstrap.ComboBox} combo This combo box
12487         * @param {Roo.data.Record} record The data record returned from the underlying store
12488         * @param {Number} index The index of the selected item in the dropdown list
12489         */
12490         'beforeselect' : true,
12491         /**
12492          * @event select
12493          * Fires when a list item is selected
12494         * @param {Roo.bootstrap.ComboBox} combo This combo box
12495         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12496         * @param {Number} index The index of the selected item in the dropdown list
12497         */
12498         'select' : true,
12499         /**
12500          * @event beforequery
12501          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12502          * The event object passed has these properties:
12503         * @param {Roo.bootstrap.ComboBox} combo This combo box
12504         * @param {String} query The query
12505         * @param {Boolean} forceAll true to force "all" query
12506         * @param {Boolean} cancel true to cancel the query
12507         * @param {Object} e The query event object
12508         */
12509         'beforequery': true,
12510          /**
12511          * @event add
12512          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12513         * @param {Roo.bootstrap.ComboBox} combo This combo box
12514         */
12515         'add' : true,
12516         /**
12517          * @event edit
12518          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12519         * @param {Roo.bootstrap.ComboBox} combo This combo box
12520         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12521         */
12522         'edit' : true,
12523         /**
12524          * @event remove
12525          * Fires when the remove value from the combobox array
12526         * @param {Roo.bootstrap.ComboBox} combo This combo box
12527         */
12528         'remove' : true,
12529         /**
12530          * @event afterremove
12531          * Fires when the remove value from the combobox array
12532         * @param {Roo.bootstrap.ComboBox} combo This combo box
12533         */
12534         'afterremove' : true,
12535         /**
12536          * @event specialfilter
12537          * Fires when specialfilter
12538             * @param {Roo.bootstrap.ComboBox} combo This combo box
12539             */
12540         'specialfilter' : true,
12541         /**
12542          * @event tick
12543          * Fires when tick the element
12544             * @param {Roo.bootstrap.ComboBox} combo This combo box
12545             */
12546         'tick' : true,
12547         /**
12548          * @event touchviewdisplay
12549          * Fires when touch view require special display (default is using displayField)
12550             * @param {Roo.bootstrap.ComboBox} combo This combo box
12551             * @param {Object} cfg set html .
12552             */
12553         'touchviewdisplay' : true
12554         
12555     });
12556     
12557     this.item = [];
12558     this.tickItems = [];
12559     
12560     this.selectedIndex = -1;
12561     if(this.mode == 'local'){
12562         if(config.queryDelay === undefined){
12563             this.queryDelay = 10;
12564         }
12565         if(config.minChars === undefined){
12566             this.minChars = 0;
12567         }
12568     }
12569 };
12570
12571 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12572      
12573     /**
12574      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12575      * rendering into an Roo.Editor, defaults to false)
12576      */
12577     /**
12578      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12579      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12580      */
12581     /**
12582      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12583      */
12584     /**
12585      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12586      * the dropdown list (defaults to undefined, with no header element)
12587      */
12588
12589      /**
12590      * @cfg {String/Roo.Template} tpl The template to use to render the output
12591      */
12592      
12593      /**
12594      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12595      */
12596     listWidth: undefined,
12597     /**
12598      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12599      * mode = 'remote' or 'text' if mode = 'local')
12600      */
12601     displayField: undefined,
12602     
12603     /**
12604      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12605      * mode = 'remote' or 'value' if mode = 'local'). 
12606      * Note: use of a valueField requires the user make a selection
12607      * in order for a value to be mapped.
12608      */
12609     valueField: undefined,
12610     /**
12611      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12612      */
12613     modalTitle : '',
12614     
12615     /**
12616      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12617      * field's data value (defaults to the underlying DOM element's name)
12618      */
12619     hiddenName: undefined,
12620     /**
12621      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12622      */
12623     listClass: '',
12624     /**
12625      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12626      */
12627     selectedClass: 'active',
12628     
12629     /**
12630      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12631      */
12632     shadow:'sides',
12633     /**
12634      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12635      * anchor positions (defaults to 'tl-bl')
12636      */
12637     listAlign: 'tl-bl?',
12638     /**
12639      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12640      */
12641     maxHeight: 300,
12642     /**
12643      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12644      * query specified by the allQuery config option (defaults to 'query')
12645      */
12646     triggerAction: 'query',
12647     /**
12648      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12649      * (defaults to 4, does not apply if editable = false)
12650      */
12651     minChars : 4,
12652     /**
12653      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12654      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12655      */
12656     typeAhead: false,
12657     /**
12658      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12659      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12660      */
12661     queryDelay: 500,
12662     /**
12663      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12664      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12665      */
12666     pageSize: 0,
12667     /**
12668      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12669      * when editable = true (defaults to false)
12670      */
12671     selectOnFocus:false,
12672     /**
12673      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12674      */
12675     queryParam: 'query',
12676     /**
12677      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12678      * when mode = 'remote' (defaults to 'Loading...')
12679      */
12680     loadingText: 'Loading...',
12681     /**
12682      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12683      */
12684     resizable: false,
12685     /**
12686      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12687      */
12688     handleHeight : 8,
12689     /**
12690      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12691      * traditional select (defaults to true)
12692      */
12693     editable: true,
12694     /**
12695      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12696      */
12697     allQuery: '',
12698     /**
12699      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12700      */
12701     mode: 'remote',
12702     /**
12703      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12704      * listWidth has a higher value)
12705      */
12706     minListWidth : 70,
12707     /**
12708      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12709      * allow the user to set arbitrary text into the field (defaults to false)
12710      */
12711     forceSelection:false,
12712     /**
12713      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12714      * if typeAhead = true (defaults to 250)
12715      */
12716     typeAheadDelay : 250,
12717     /**
12718      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12719      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12720      */
12721     valueNotFoundText : undefined,
12722     /**
12723      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12724      */
12725     blockFocus : false,
12726     
12727     /**
12728      * @cfg {Boolean} disableClear Disable showing of clear button.
12729      */
12730     disableClear : false,
12731     /**
12732      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12733      */
12734     alwaysQuery : false,
12735     
12736     /**
12737      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12738      */
12739     multiple : false,
12740     
12741     /**
12742      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12743      */
12744     invalidClass : "has-warning",
12745     
12746     /**
12747      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12748      */
12749     validClass : "has-success",
12750     
12751     /**
12752      * @cfg {Boolean} specialFilter (true|false) special filter default false
12753      */
12754     specialFilter : false,
12755     
12756     /**
12757      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12758      */
12759     mobileTouchView : true,
12760     
12761     /**
12762      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12763      */
12764     useNativeIOS : false,
12765     
12766     ios_options : false,
12767     
12768     //private
12769     addicon : false,
12770     editicon: false,
12771     
12772     page: 0,
12773     hasQuery: false,
12774     append: false,
12775     loadNext: false,
12776     autoFocus : true,
12777     tickable : false,
12778     btnPosition : 'right',
12779     triggerList : true,
12780     showToggleBtn : true,
12781     animate : true,
12782     emptyResultText: 'Empty',
12783     triggerText : 'Select',
12784     emptyTitle : '',
12785     
12786     // element that contains real text value.. (when hidden is used..)
12787     
12788     getAutoCreate : function()
12789     {   
12790         var cfg = false;
12791         //render
12792         /*
12793          * Render classic select for iso
12794          */
12795         
12796         if(Roo.isIOS && this.useNativeIOS){
12797             cfg = this.getAutoCreateNativeIOS();
12798             return cfg;
12799         }
12800         
12801         /*
12802          * Touch Devices
12803          */
12804         
12805         if(Roo.isTouch && this.mobileTouchView){
12806             cfg = this.getAutoCreateTouchView();
12807             return cfg;;
12808         }
12809         
12810         /*
12811          *  Normal ComboBox
12812          */
12813         if(!this.tickable){
12814             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12815             return cfg;
12816         }
12817         
12818         /*
12819          *  ComboBox with tickable selections
12820          */
12821              
12822         var align = this.labelAlign || this.parentLabelAlign();
12823         
12824         cfg = {
12825             cls : 'form-group roo-combobox-tickable' //input-group
12826         };
12827         
12828         var btn_text_select = '';
12829         var btn_text_done = '';
12830         var btn_text_cancel = '';
12831         
12832         if (this.btn_text_show) {
12833             btn_text_select = 'Select';
12834             btn_text_done = 'Done';
12835             btn_text_cancel = 'Cancel'; 
12836         }
12837         
12838         var buttons = {
12839             tag : 'div',
12840             cls : 'tickable-buttons',
12841             cn : [
12842                 {
12843                     tag : 'button',
12844                     type : 'button',
12845                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12846                     //html : this.triggerText
12847                     html: btn_text_select
12848                 },
12849                 {
12850                     tag : 'button',
12851                     type : 'button',
12852                     name : 'ok',
12853                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12854                     //html : 'Done'
12855                     html: btn_text_done
12856                 },
12857                 {
12858                     tag : 'button',
12859                     type : 'button',
12860                     name : 'cancel',
12861                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12862                     //html : 'Cancel'
12863                     html: btn_text_cancel
12864                 }
12865             ]
12866         };
12867         
12868         if(this.editable){
12869             buttons.cn.unshift({
12870                 tag: 'input',
12871                 cls: 'roo-select2-search-field-input'
12872             });
12873         }
12874         
12875         var _this = this;
12876         
12877         Roo.each(buttons.cn, function(c){
12878             if (_this.size) {
12879                 c.cls += ' btn-' + _this.size;
12880             }
12881
12882             if (_this.disabled) {
12883                 c.disabled = true;
12884             }
12885         });
12886         
12887         var box = {
12888             tag: 'div',
12889             cn: [
12890                 {
12891                     tag: 'input',
12892                     type : 'hidden',
12893                     cls: 'form-hidden-field'
12894                 },
12895                 {
12896                     tag: 'ul',
12897                     cls: 'roo-select2-choices',
12898                     cn:[
12899                         {
12900                             tag: 'li',
12901                             cls: 'roo-select2-search-field',
12902                             cn: [
12903                                 buttons
12904                             ]
12905                         }
12906                     ]
12907                 }
12908             ]
12909         };
12910         
12911         var combobox = {
12912             cls: 'roo-select2-container input-group roo-select2-container-multi',
12913             cn: [
12914                 box
12915 //                {
12916 //                    tag: 'ul',
12917 //                    cls: 'typeahead typeahead-long dropdown-menu',
12918 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12919 //                }
12920             ]
12921         };
12922         
12923         if(this.hasFeedback && !this.allowBlank){
12924             
12925             var feedback = {
12926                 tag: 'span',
12927                 cls: 'glyphicon form-control-feedback'
12928             };
12929
12930             combobox.cn.push(feedback);
12931         }
12932         
12933         
12934         if (align ==='left' && this.fieldLabel.length) {
12935             
12936             cfg.cls += ' roo-form-group-label-left';
12937             
12938             cfg.cn = [
12939                 {
12940                     tag : 'i',
12941                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12942                     tooltip : 'This field is required'
12943                 },
12944                 {
12945                     tag: 'label',
12946                     'for' :  id,
12947                     cls : 'control-label',
12948                     html : this.fieldLabel
12949
12950                 },
12951                 {
12952                     cls : "", 
12953                     cn: [
12954                         combobox
12955                     ]
12956                 }
12957
12958             ];
12959             
12960             var labelCfg = cfg.cn[1];
12961             var contentCfg = cfg.cn[2];
12962             
12963
12964             if(this.indicatorpos == 'right'){
12965                 
12966                 cfg.cn = [
12967                     {
12968                         tag: 'label',
12969                         'for' :  id,
12970                         cls : 'control-label',
12971                         cn : [
12972                             {
12973                                 tag : 'span',
12974                                 html : this.fieldLabel
12975                             },
12976                             {
12977                                 tag : 'i',
12978                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12979                                 tooltip : 'This field is required'
12980                             }
12981                         ]
12982                     },
12983                     {
12984                         cls : "",
12985                         cn: [
12986                             combobox
12987                         ]
12988                     }
12989
12990                 ];
12991                 
12992                 
12993                 
12994                 labelCfg = cfg.cn[0];
12995                 contentCfg = cfg.cn[1];
12996             
12997             }
12998             
12999             if(this.labelWidth > 12){
13000                 labelCfg.style = "width: " + this.labelWidth + 'px';
13001             }
13002             
13003             if(this.labelWidth < 13 && this.labelmd == 0){
13004                 this.labelmd = this.labelWidth;
13005             }
13006             
13007             if(this.labellg > 0){
13008                 labelCfg.cls += ' col-lg-' + this.labellg;
13009                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13010             }
13011             
13012             if(this.labelmd > 0){
13013                 labelCfg.cls += ' col-md-' + this.labelmd;
13014                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13015             }
13016             
13017             if(this.labelsm > 0){
13018                 labelCfg.cls += ' col-sm-' + this.labelsm;
13019                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13020             }
13021             
13022             if(this.labelxs > 0){
13023                 labelCfg.cls += ' col-xs-' + this.labelxs;
13024                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13025             }
13026                 
13027                 
13028         } else if ( this.fieldLabel.length) {
13029 //                Roo.log(" label");
13030                  cfg.cn = [
13031                     {
13032                         tag : 'i',
13033                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13034                         tooltip : 'This field is required'
13035                     },
13036                     {
13037                         tag: 'label',
13038                         //cls : 'input-group-addon',
13039                         html : this.fieldLabel
13040                     },
13041                     combobox
13042                 ];
13043                 
13044                 if(this.indicatorpos == 'right'){
13045                     cfg.cn = [
13046                         {
13047                             tag: 'label',
13048                             //cls : 'input-group-addon',
13049                             html : this.fieldLabel
13050                         },
13051                         {
13052                             tag : 'i',
13053                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13054                             tooltip : 'This field is required'
13055                         },
13056                         combobox
13057                     ];
13058                     
13059                 }
13060
13061         } else {
13062             
13063 //                Roo.log(" no label && no align");
13064                 cfg = combobox
13065                      
13066                 
13067         }
13068          
13069         var settings=this;
13070         ['xs','sm','md','lg'].map(function(size){
13071             if (settings[size]) {
13072                 cfg.cls += ' col-' + size + '-' + settings[size];
13073             }
13074         });
13075         
13076         return cfg;
13077         
13078     },
13079     
13080     _initEventsCalled : false,
13081     
13082     // private
13083     initEvents: function()
13084     {   
13085         if (this._initEventsCalled) { // as we call render... prevent looping...
13086             return;
13087         }
13088         this._initEventsCalled = true;
13089         
13090         if (!this.store) {
13091             throw "can not find store for combo";
13092         }
13093         
13094         this.indicator = this.indicatorEl();
13095         
13096         this.store = Roo.factory(this.store, Roo.data);
13097         this.store.parent = this;
13098         
13099         // if we are building from html. then this element is so complex, that we can not really
13100         // use the rendered HTML.
13101         // so we have to trash and replace the previous code.
13102         if (Roo.XComponent.build_from_html) {
13103             // remove this element....
13104             var e = this.el.dom, k=0;
13105             while (e ) { e = e.previousSibling;  ++k;}
13106
13107             this.el.remove();
13108             
13109             this.el=false;
13110             this.rendered = false;
13111             
13112             this.render(this.parent().getChildContainer(true), k);
13113         }
13114         
13115         if(Roo.isIOS && this.useNativeIOS){
13116             this.initIOSView();
13117             return;
13118         }
13119         
13120         /*
13121          * Touch Devices
13122          */
13123         
13124         if(Roo.isTouch && this.mobileTouchView){
13125             this.initTouchView();
13126             return;
13127         }
13128         
13129         if(this.tickable){
13130             this.initTickableEvents();
13131             return;
13132         }
13133         
13134         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13135         
13136         if(this.hiddenName){
13137             
13138             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13139             
13140             this.hiddenField.dom.value =
13141                 this.hiddenValue !== undefined ? this.hiddenValue :
13142                 this.value !== undefined ? this.value : '';
13143
13144             // prevent input submission
13145             this.el.dom.removeAttribute('name');
13146             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13147              
13148              
13149         }
13150         //if(Roo.isGecko){
13151         //    this.el.dom.setAttribute('autocomplete', 'off');
13152         //}
13153         
13154         var cls = 'x-combo-list';
13155         
13156         //this.list = new Roo.Layer({
13157         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13158         //});
13159         
13160         var _this = this;
13161         
13162         (function(){
13163             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13164             _this.list.setWidth(lw);
13165         }).defer(100);
13166         
13167         this.list.on('mouseover', this.onViewOver, this);
13168         this.list.on('mousemove', this.onViewMove, this);
13169         this.list.on('scroll', this.onViewScroll, this);
13170         
13171         /*
13172         this.list.swallowEvent('mousewheel');
13173         this.assetHeight = 0;
13174
13175         if(this.title){
13176             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13177             this.assetHeight += this.header.getHeight();
13178         }
13179
13180         this.innerList = this.list.createChild({cls:cls+'-inner'});
13181         this.innerList.on('mouseover', this.onViewOver, this);
13182         this.innerList.on('mousemove', this.onViewMove, this);
13183         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13184         
13185         if(this.allowBlank && !this.pageSize && !this.disableClear){
13186             this.footer = this.list.createChild({cls:cls+'-ft'});
13187             this.pageTb = new Roo.Toolbar(this.footer);
13188            
13189         }
13190         if(this.pageSize){
13191             this.footer = this.list.createChild({cls:cls+'-ft'});
13192             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13193                     {pageSize: this.pageSize});
13194             
13195         }
13196         
13197         if (this.pageTb && this.allowBlank && !this.disableClear) {
13198             var _this = this;
13199             this.pageTb.add(new Roo.Toolbar.Fill(), {
13200                 cls: 'x-btn-icon x-btn-clear',
13201                 text: '&#160;',
13202                 handler: function()
13203                 {
13204                     _this.collapse();
13205                     _this.clearValue();
13206                     _this.onSelect(false, -1);
13207                 }
13208             });
13209         }
13210         if (this.footer) {
13211             this.assetHeight += this.footer.getHeight();
13212         }
13213         */
13214             
13215         if(!this.tpl){
13216             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13217         }
13218
13219         this.view = new Roo.View(this.list, this.tpl, {
13220             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13221         });
13222         //this.view.wrapEl.setDisplayed(false);
13223         this.view.on('click', this.onViewClick, this);
13224         
13225         
13226         this.store.on('beforeload', this.onBeforeLoad, this);
13227         this.store.on('load', this.onLoad, this);
13228         this.store.on('loadexception', this.onLoadException, this);
13229         /*
13230         if(this.resizable){
13231             this.resizer = new Roo.Resizable(this.list,  {
13232                pinned:true, handles:'se'
13233             });
13234             this.resizer.on('resize', function(r, w, h){
13235                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13236                 this.listWidth = w;
13237                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13238                 this.restrictHeight();
13239             }, this);
13240             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13241         }
13242         */
13243         if(!this.editable){
13244             this.editable = true;
13245             this.setEditable(false);
13246         }
13247         
13248         /*
13249         
13250         if (typeof(this.events.add.listeners) != 'undefined') {
13251             
13252             this.addicon = this.wrap.createChild(
13253                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13254        
13255             this.addicon.on('click', function(e) {
13256                 this.fireEvent('add', this);
13257             }, this);
13258         }
13259         if (typeof(this.events.edit.listeners) != 'undefined') {
13260             
13261             this.editicon = this.wrap.createChild(
13262                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13263             if (this.addicon) {
13264                 this.editicon.setStyle('margin-left', '40px');
13265             }
13266             this.editicon.on('click', function(e) {
13267                 
13268                 // we fire even  if inothing is selected..
13269                 this.fireEvent('edit', this, this.lastData );
13270                 
13271             }, this);
13272         }
13273         */
13274         
13275         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13276             "up" : function(e){
13277                 this.inKeyMode = true;
13278                 this.selectPrev();
13279             },
13280
13281             "down" : function(e){
13282                 if(!this.isExpanded()){
13283                     this.onTriggerClick();
13284                 }else{
13285                     this.inKeyMode = true;
13286                     this.selectNext();
13287                 }
13288             },
13289
13290             "enter" : function(e){
13291 //                this.onViewClick();
13292                 //return true;
13293                 this.collapse();
13294                 
13295                 if(this.fireEvent("specialkey", this, e)){
13296                     this.onViewClick(false);
13297                 }
13298                 
13299                 return true;
13300             },
13301
13302             "esc" : function(e){
13303                 this.collapse();
13304             },
13305
13306             "tab" : function(e){
13307                 this.collapse();
13308                 
13309                 if(this.fireEvent("specialkey", this, e)){
13310                     this.onViewClick(false);
13311                 }
13312                 
13313                 return true;
13314             },
13315
13316             scope : this,
13317
13318             doRelay : function(foo, bar, hname){
13319                 if(hname == 'down' || this.scope.isExpanded()){
13320                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13321                 }
13322                 return true;
13323             },
13324
13325             forceKeyDown: true
13326         });
13327         
13328         
13329         this.queryDelay = Math.max(this.queryDelay || 10,
13330                 this.mode == 'local' ? 10 : 250);
13331         
13332         
13333         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13334         
13335         if(this.typeAhead){
13336             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13337         }
13338         if(this.editable !== false){
13339             this.inputEl().on("keyup", this.onKeyUp, this);
13340         }
13341         if(this.forceSelection){
13342             this.inputEl().on('blur', this.doForce, this);
13343         }
13344         
13345         if(this.multiple){
13346             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13347             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13348         }
13349     },
13350     
13351     initTickableEvents: function()
13352     {   
13353         this.createList();
13354         
13355         if(this.hiddenName){
13356             
13357             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13358             
13359             this.hiddenField.dom.value =
13360                 this.hiddenValue !== undefined ? this.hiddenValue :
13361                 this.value !== undefined ? this.value : '';
13362
13363             // prevent input submission
13364             this.el.dom.removeAttribute('name');
13365             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13366              
13367              
13368         }
13369         
13370 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13371         
13372         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13373         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13374         if(this.triggerList){
13375             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13376         }
13377          
13378         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13379         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13380         
13381         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13382         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13383         
13384         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13385         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13386         
13387         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13388         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13389         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13390         
13391         this.okBtn.hide();
13392         this.cancelBtn.hide();
13393         
13394         var _this = this;
13395         
13396         (function(){
13397             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13398             _this.list.setWidth(lw);
13399         }).defer(100);
13400         
13401         this.list.on('mouseover', this.onViewOver, this);
13402         this.list.on('mousemove', this.onViewMove, this);
13403         
13404         this.list.on('scroll', this.onViewScroll, this);
13405         
13406         if(!this.tpl){
13407             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>';
13408         }
13409
13410         this.view = new Roo.View(this.list, this.tpl, {
13411             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13412         });
13413         
13414         //this.view.wrapEl.setDisplayed(false);
13415         this.view.on('click', this.onViewClick, this);
13416         
13417         
13418         
13419         this.store.on('beforeload', this.onBeforeLoad, this);
13420         this.store.on('load', this.onLoad, this);
13421         this.store.on('loadexception', this.onLoadException, this);
13422         
13423         if(this.editable){
13424             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13425                 "up" : function(e){
13426                     this.inKeyMode = true;
13427                     this.selectPrev();
13428                 },
13429
13430                 "down" : function(e){
13431                     this.inKeyMode = true;
13432                     this.selectNext();
13433                 },
13434
13435                 "enter" : function(e){
13436                     if(this.fireEvent("specialkey", this, e)){
13437                         this.onViewClick(false);
13438                     }
13439                     
13440                     return true;
13441                 },
13442
13443                 "esc" : function(e){
13444                     this.onTickableFooterButtonClick(e, false, false);
13445                 },
13446
13447                 "tab" : function(e){
13448                     this.fireEvent("specialkey", this, e);
13449                     
13450                     this.onTickableFooterButtonClick(e, false, false);
13451                     
13452                     return true;
13453                 },
13454
13455                 scope : this,
13456
13457                 doRelay : function(e, fn, key){
13458                     if(this.scope.isExpanded()){
13459                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13460                     }
13461                     return true;
13462                 },
13463
13464                 forceKeyDown: true
13465             });
13466         }
13467         
13468         this.queryDelay = Math.max(this.queryDelay || 10,
13469                 this.mode == 'local' ? 10 : 250);
13470         
13471         
13472         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13473         
13474         if(this.typeAhead){
13475             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13476         }
13477         
13478         if(this.editable !== false){
13479             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13480         }
13481         
13482         this.indicator = this.indicatorEl();
13483         
13484         if(this.indicator){
13485             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13486             this.indicator.hide();
13487         }
13488         
13489     },
13490
13491     onDestroy : function(){
13492         if(this.view){
13493             this.view.setStore(null);
13494             this.view.el.removeAllListeners();
13495             this.view.el.remove();
13496             this.view.purgeListeners();
13497         }
13498         if(this.list){
13499             this.list.dom.innerHTML  = '';
13500         }
13501         
13502         if(this.store){
13503             this.store.un('beforeload', this.onBeforeLoad, this);
13504             this.store.un('load', this.onLoad, this);
13505             this.store.un('loadexception', this.onLoadException, this);
13506         }
13507         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13508     },
13509
13510     // private
13511     fireKey : function(e){
13512         if(e.isNavKeyPress() && !this.list.isVisible()){
13513             this.fireEvent("specialkey", this, e);
13514         }
13515     },
13516
13517     // private
13518     onResize: function(w, h){
13519 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13520 //        
13521 //        if(typeof w != 'number'){
13522 //            // we do not handle it!?!?
13523 //            return;
13524 //        }
13525 //        var tw = this.trigger.getWidth();
13526 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13527 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13528 //        var x = w - tw;
13529 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13530 //            
13531 //        //this.trigger.setStyle('left', x+'px');
13532 //        
13533 //        if(this.list && this.listWidth === undefined){
13534 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13535 //            this.list.setWidth(lw);
13536 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13537 //        }
13538         
13539     
13540         
13541     },
13542
13543     /**
13544      * Allow or prevent the user from directly editing the field text.  If false is passed,
13545      * the user will only be able to select from the items defined in the dropdown list.  This method
13546      * is the runtime equivalent of setting the 'editable' config option at config time.
13547      * @param {Boolean} value True to allow the user to directly edit the field text
13548      */
13549     setEditable : function(value){
13550         if(value == this.editable){
13551             return;
13552         }
13553         this.editable = value;
13554         if(!value){
13555             this.inputEl().dom.setAttribute('readOnly', true);
13556             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13557             this.inputEl().addClass('x-combo-noedit');
13558         }else{
13559             this.inputEl().dom.setAttribute('readOnly', false);
13560             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13561             this.inputEl().removeClass('x-combo-noedit');
13562         }
13563     },
13564
13565     // private
13566     
13567     onBeforeLoad : function(combo,opts){
13568         if(!this.hasFocus){
13569             return;
13570         }
13571          if (!opts.add) {
13572             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13573          }
13574         this.restrictHeight();
13575         this.selectedIndex = -1;
13576     },
13577
13578     // private
13579     onLoad : function(){
13580         
13581         this.hasQuery = false;
13582         
13583         if(!this.hasFocus){
13584             return;
13585         }
13586         
13587         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13588             this.loading.hide();
13589         }
13590         
13591         if(this.store.getCount() > 0){
13592             
13593             this.expand();
13594             this.restrictHeight();
13595             if(this.lastQuery == this.allQuery){
13596                 if(this.editable && !this.tickable){
13597                     this.inputEl().dom.select();
13598                 }
13599                 
13600                 if(
13601                     !this.selectByValue(this.value, true) &&
13602                     this.autoFocus && 
13603                     (
13604                         !this.store.lastOptions ||
13605                         typeof(this.store.lastOptions.add) == 'undefined' || 
13606                         this.store.lastOptions.add != true
13607                     )
13608                 ){
13609                     this.select(0, true);
13610                 }
13611             }else{
13612                 if(this.autoFocus){
13613                     this.selectNext();
13614                 }
13615                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13616                     this.taTask.delay(this.typeAheadDelay);
13617                 }
13618             }
13619         }else{
13620             this.onEmptyResults();
13621         }
13622         
13623         //this.el.focus();
13624     },
13625     // private
13626     onLoadException : function()
13627     {
13628         this.hasQuery = false;
13629         
13630         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13631             this.loading.hide();
13632         }
13633         
13634         if(this.tickable && this.editable){
13635             return;
13636         }
13637         
13638         this.collapse();
13639         // only causes errors at present
13640         //Roo.log(this.store.reader.jsonData);
13641         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13642             // fixme
13643             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13644         //}
13645         
13646         
13647     },
13648     // private
13649     onTypeAhead : function(){
13650         if(this.store.getCount() > 0){
13651             var r = this.store.getAt(0);
13652             var newValue = r.data[this.displayField];
13653             var len = newValue.length;
13654             var selStart = this.getRawValue().length;
13655             
13656             if(selStart != len){
13657                 this.setRawValue(newValue);
13658                 this.selectText(selStart, newValue.length);
13659             }
13660         }
13661     },
13662
13663     // private
13664     onSelect : function(record, index){
13665         
13666         if(this.fireEvent('beforeselect', this, record, index) !== false){
13667         
13668             this.setFromData(index > -1 ? record.data : false);
13669             
13670             this.collapse();
13671             this.fireEvent('select', this, record, index);
13672         }
13673     },
13674
13675     /**
13676      * Returns the currently selected field value or empty string if no value is set.
13677      * @return {String} value The selected value
13678      */
13679     getValue : function()
13680     {
13681         if(Roo.isIOS && this.useNativeIOS){
13682             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13683         }
13684         
13685         if(this.multiple){
13686             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13687         }
13688         
13689         if(this.valueField){
13690             return typeof this.value != 'undefined' ? this.value : '';
13691         }else{
13692             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13693         }
13694     },
13695     
13696     getRawValue : function()
13697     {
13698         if(Roo.isIOS && this.useNativeIOS){
13699             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13700         }
13701         
13702         var v = this.inputEl().getValue();
13703         
13704         return v;
13705     },
13706
13707     /**
13708      * Clears any text/value currently set in the field
13709      */
13710     clearValue : function(){
13711         
13712         if(this.hiddenField){
13713             this.hiddenField.dom.value = '';
13714         }
13715         this.value = '';
13716         this.setRawValue('');
13717         this.lastSelectionText = '';
13718         this.lastData = false;
13719         
13720         var close = this.closeTriggerEl();
13721         
13722         if(close){
13723             close.hide();
13724         }
13725         
13726         this.validate();
13727         
13728     },
13729
13730     /**
13731      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13732      * will be displayed in the field.  If the value does not match the data value of an existing item,
13733      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13734      * Otherwise the field will be blank (although the value will still be set).
13735      * @param {String} value The value to match
13736      */
13737     setValue : function(v)
13738     {
13739         if(Roo.isIOS && this.useNativeIOS){
13740             this.setIOSValue(v);
13741             return;
13742         }
13743         
13744         if(this.multiple){
13745             this.syncValue();
13746             return;
13747         }
13748         
13749         var text = v;
13750         if(this.valueField){
13751             var r = this.findRecord(this.valueField, v);
13752             if(r){
13753                 text = r.data[this.displayField];
13754             }else if(this.valueNotFoundText !== undefined){
13755                 text = this.valueNotFoundText;
13756             }
13757         }
13758         this.lastSelectionText = text;
13759         if(this.hiddenField){
13760             this.hiddenField.dom.value = v;
13761         }
13762         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13763         this.value = v;
13764         
13765         var close = this.closeTriggerEl();
13766         
13767         if(close){
13768             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13769         }
13770         
13771         this.validate();
13772     },
13773     /**
13774      * @property {Object} the last set data for the element
13775      */
13776     
13777     lastData : false,
13778     /**
13779      * Sets the value of the field based on a object which is related to the record format for the store.
13780      * @param {Object} value the value to set as. or false on reset?
13781      */
13782     setFromData : function(o){
13783         
13784         if(this.multiple){
13785             this.addItem(o);
13786             return;
13787         }
13788             
13789         var dv = ''; // display value
13790         var vv = ''; // value value..
13791         this.lastData = o;
13792         if (this.displayField) {
13793             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13794         } else {
13795             // this is an error condition!!!
13796             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13797         }
13798         
13799         if(this.valueField){
13800             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13801         }
13802         
13803         var close = this.closeTriggerEl();
13804         
13805         if(close){
13806             if(dv.length || vv * 1 > 0){
13807                 close.show() ;
13808                 this.blockFocus=true;
13809             } else {
13810                 close.hide();
13811             }             
13812         }
13813         
13814         if(this.hiddenField){
13815             this.hiddenField.dom.value = vv;
13816             
13817             this.lastSelectionText = dv;
13818             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13819             this.value = vv;
13820             return;
13821         }
13822         // no hidden field.. - we store the value in 'value', but still display
13823         // display field!!!!
13824         this.lastSelectionText = dv;
13825         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13826         this.value = vv;
13827         
13828         
13829         
13830     },
13831     // private
13832     reset : function(){
13833         // overridden so that last data is reset..
13834         
13835         if(this.multiple){
13836             this.clearItem();
13837             return;
13838         }
13839         
13840         this.setValue(this.originalValue);
13841         //this.clearInvalid();
13842         this.lastData = false;
13843         if (this.view) {
13844             this.view.clearSelections();
13845         }
13846         
13847         this.validate();
13848     },
13849     // private
13850     findRecord : function(prop, value){
13851         var record;
13852         if(this.store.getCount() > 0){
13853             this.store.each(function(r){
13854                 if(r.data[prop] == value){
13855                     record = r;
13856                     return false;
13857                 }
13858                 return true;
13859             });
13860         }
13861         return record;
13862     },
13863     
13864     getName: function()
13865     {
13866         // returns hidden if it's set..
13867         if (!this.rendered) {return ''};
13868         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13869         
13870     },
13871     // private
13872     onViewMove : function(e, t){
13873         this.inKeyMode = false;
13874     },
13875
13876     // private
13877     onViewOver : function(e, t){
13878         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13879             return;
13880         }
13881         var item = this.view.findItemFromChild(t);
13882         
13883         if(item){
13884             var index = this.view.indexOf(item);
13885             this.select(index, false);
13886         }
13887     },
13888
13889     // private
13890     onViewClick : function(view, doFocus, el, e)
13891     {
13892         var index = this.view.getSelectedIndexes()[0];
13893         
13894         var r = this.store.getAt(index);
13895         
13896         if(this.tickable){
13897             
13898             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13899                 return;
13900             }
13901             
13902             var rm = false;
13903             var _this = this;
13904             
13905             Roo.each(this.tickItems, function(v,k){
13906                 
13907                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13908                     Roo.log(v);
13909                     _this.tickItems.splice(k, 1);
13910                     
13911                     if(typeof(e) == 'undefined' && view == false){
13912                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13913                     }
13914                     
13915                     rm = true;
13916                     return;
13917                 }
13918             });
13919             
13920             if(rm){
13921                 return;
13922             }
13923             
13924             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13925                 this.tickItems.push(r.data);
13926             }
13927             
13928             if(typeof(e) == 'undefined' && view == false){
13929                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13930             }
13931                     
13932             return;
13933         }
13934         
13935         if(r){
13936             this.onSelect(r, index);
13937         }
13938         if(doFocus !== false && !this.blockFocus){
13939             this.inputEl().focus();
13940         }
13941     },
13942
13943     // private
13944     restrictHeight : function(){
13945         //this.innerList.dom.style.height = '';
13946         //var inner = this.innerList.dom;
13947         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13948         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13949         //this.list.beginUpdate();
13950         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13951         this.list.alignTo(this.inputEl(), this.listAlign);
13952         this.list.alignTo(this.inputEl(), this.listAlign);
13953         //this.list.endUpdate();
13954     },
13955
13956     // private
13957     onEmptyResults : function(){
13958         
13959         if(this.tickable && this.editable){
13960             this.restrictHeight();
13961             return;
13962         }
13963         
13964         this.collapse();
13965     },
13966
13967     /**
13968      * Returns true if the dropdown list is expanded, else false.
13969      */
13970     isExpanded : function(){
13971         return this.list.isVisible();
13972     },
13973
13974     /**
13975      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13976      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13977      * @param {String} value The data value of the item to select
13978      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13979      * selected item if it is not currently in view (defaults to true)
13980      * @return {Boolean} True if the value matched an item in the list, else false
13981      */
13982     selectByValue : function(v, scrollIntoView){
13983         if(v !== undefined && v !== null){
13984             var r = this.findRecord(this.valueField || this.displayField, v);
13985             if(r){
13986                 this.select(this.store.indexOf(r), scrollIntoView);
13987                 return true;
13988             }
13989         }
13990         return false;
13991     },
13992
13993     /**
13994      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13995      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13996      * @param {Number} index The zero-based index of the list item to select
13997      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13998      * selected item if it is not currently in view (defaults to true)
13999      */
14000     select : function(index, scrollIntoView){
14001         this.selectedIndex = index;
14002         this.view.select(index);
14003         if(scrollIntoView !== false){
14004             var el = this.view.getNode(index);
14005             /*
14006              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14007              */
14008             if(el){
14009                 this.list.scrollChildIntoView(el, false);
14010             }
14011         }
14012     },
14013
14014     // private
14015     selectNext : function(){
14016         var ct = this.store.getCount();
14017         if(ct > 0){
14018             if(this.selectedIndex == -1){
14019                 this.select(0);
14020             }else if(this.selectedIndex < ct-1){
14021                 this.select(this.selectedIndex+1);
14022             }
14023         }
14024     },
14025
14026     // private
14027     selectPrev : function(){
14028         var ct = this.store.getCount();
14029         if(ct > 0){
14030             if(this.selectedIndex == -1){
14031                 this.select(0);
14032             }else if(this.selectedIndex != 0){
14033                 this.select(this.selectedIndex-1);
14034             }
14035         }
14036     },
14037
14038     // private
14039     onKeyUp : function(e){
14040         if(this.editable !== false && !e.isSpecialKey()){
14041             this.lastKey = e.getKey();
14042             this.dqTask.delay(this.queryDelay);
14043         }
14044     },
14045
14046     // private
14047     validateBlur : function(){
14048         return !this.list || !this.list.isVisible();   
14049     },
14050
14051     // private
14052     initQuery : function(){
14053         
14054         var v = this.getRawValue();
14055         
14056         if(this.tickable && this.editable){
14057             v = this.tickableInputEl().getValue();
14058         }
14059         
14060         this.doQuery(v);
14061     },
14062
14063     // private
14064     doForce : function(){
14065         if(this.inputEl().dom.value.length > 0){
14066             this.inputEl().dom.value =
14067                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14068              
14069         }
14070     },
14071
14072     /**
14073      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14074      * query allowing the query action to be canceled if needed.
14075      * @param {String} query The SQL query to execute
14076      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14077      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14078      * saved in the current store (defaults to false)
14079      */
14080     doQuery : function(q, forceAll){
14081         
14082         if(q === undefined || q === null){
14083             q = '';
14084         }
14085         var qe = {
14086             query: q,
14087             forceAll: forceAll,
14088             combo: this,
14089             cancel:false
14090         };
14091         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14092             return false;
14093         }
14094         q = qe.query;
14095         
14096         forceAll = qe.forceAll;
14097         if(forceAll === true || (q.length >= this.minChars)){
14098             
14099             this.hasQuery = true;
14100             
14101             if(this.lastQuery != q || this.alwaysQuery){
14102                 this.lastQuery = q;
14103                 if(this.mode == 'local'){
14104                     this.selectedIndex = -1;
14105                     if(forceAll){
14106                         this.store.clearFilter();
14107                     }else{
14108                         
14109                         if(this.specialFilter){
14110                             this.fireEvent('specialfilter', this);
14111                             this.onLoad();
14112                             return;
14113                         }
14114                         
14115                         this.store.filter(this.displayField, q);
14116                     }
14117                     
14118                     this.store.fireEvent("datachanged", this.store);
14119                     
14120                     this.onLoad();
14121                     
14122                     
14123                 }else{
14124                     
14125                     this.store.baseParams[this.queryParam] = q;
14126                     
14127                     var options = {params : this.getParams(q)};
14128                     
14129                     if(this.loadNext){
14130                         options.add = true;
14131                         options.params.start = this.page * this.pageSize;
14132                     }
14133                     
14134                     this.store.load(options);
14135                     
14136                     /*
14137                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14138                      *  we should expand the list on onLoad
14139                      *  so command out it
14140                      */
14141 //                    this.expand();
14142                 }
14143             }else{
14144                 this.selectedIndex = -1;
14145                 this.onLoad();   
14146             }
14147         }
14148         
14149         this.loadNext = false;
14150     },
14151     
14152     // private
14153     getParams : function(q){
14154         var p = {};
14155         //p[this.queryParam] = q;
14156         
14157         if(this.pageSize){
14158             p.start = 0;
14159             p.limit = this.pageSize;
14160         }
14161         return p;
14162     },
14163
14164     /**
14165      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14166      */
14167     collapse : function(){
14168         if(!this.isExpanded()){
14169             return;
14170         }
14171         
14172         this.list.hide();
14173         
14174         this.hasFocus = false;
14175         
14176         if(this.tickable){
14177             this.okBtn.hide();
14178             this.cancelBtn.hide();
14179             this.trigger.show();
14180             
14181             if(this.editable){
14182                 this.tickableInputEl().dom.value = '';
14183                 this.tickableInputEl().blur();
14184             }
14185             
14186         }
14187         
14188         Roo.get(document).un('mousedown', this.collapseIf, this);
14189         Roo.get(document).un('mousewheel', this.collapseIf, this);
14190         if (!this.editable) {
14191             Roo.get(document).un('keydown', this.listKeyPress, this);
14192         }
14193         this.fireEvent('collapse', this);
14194         
14195         this.validate();
14196     },
14197
14198     // private
14199     collapseIf : function(e){
14200         var in_combo  = e.within(this.el);
14201         var in_list =  e.within(this.list);
14202         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14203         
14204         if (in_combo || in_list || is_list) {
14205             //e.stopPropagation();
14206             return;
14207         }
14208         
14209         if(this.tickable){
14210             this.onTickableFooterButtonClick(e, false, false);
14211         }
14212
14213         this.collapse();
14214         
14215     },
14216
14217     /**
14218      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14219      */
14220     expand : function(){
14221        
14222         if(this.isExpanded() || !this.hasFocus){
14223             return;
14224         }
14225         
14226         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14227         this.list.setWidth(lw);
14228         
14229         Roo.log('expand');
14230         
14231         this.list.show();
14232         
14233         this.restrictHeight();
14234         
14235         if(this.tickable){
14236             
14237             this.tickItems = Roo.apply([], this.item);
14238             
14239             this.okBtn.show();
14240             this.cancelBtn.show();
14241             this.trigger.hide();
14242             
14243             if(this.editable){
14244                 this.tickableInputEl().focus();
14245             }
14246             
14247         }
14248         
14249         Roo.get(document).on('mousedown', this.collapseIf, this);
14250         Roo.get(document).on('mousewheel', this.collapseIf, this);
14251         if (!this.editable) {
14252             Roo.get(document).on('keydown', this.listKeyPress, this);
14253         }
14254         
14255         this.fireEvent('expand', this);
14256     },
14257
14258     // private
14259     // Implements the default empty TriggerField.onTriggerClick function
14260     onTriggerClick : function(e)
14261     {
14262         Roo.log('trigger click');
14263         
14264         if(this.disabled || !this.triggerList){
14265             return;
14266         }
14267         
14268         this.page = 0;
14269         this.loadNext = false;
14270         
14271         if(this.isExpanded()){
14272             this.collapse();
14273             if (!this.blockFocus) {
14274                 this.inputEl().focus();
14275             }
14276             
14277         }else {
14278             this.hasFocus = true;
14279             if(this.triggerAction == 'all') {
14280                 this.doQuery(this.allQuery, true);
14281             } else {
14282                 this.doQuery(this.getRawValue());
14283             }
14284             if (!this.blockFocus) {
14285                 this.inputEl().focus();
14286             }
14287         }
14288     },
14289     
14290     onTickableTriggerClick : function(e)
14291     {
14292         if(this.disabled){
14293             return;
14294         }
14295         
14296         this.page = 0;
14297         this.loadNext = false;
14298         this.hasFocus = true;
14299         
14300         if(this.triggerAction == 'all') {
14301             this.doQuery(this.allQuery, true);
14302         } else {
14303             this.doQuery(this.getRawValue());
14304         }
14305     },
14306     
14307     onSearchFieldClick : function(e)
14308     {
14309         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14310             this.onTickableFooterButtonClick(e, false, false);
14311             return;
14312         }
14313         
14314         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14315             return;
14316         }
14317         
14318         this.page = 0;
14319         this.loadNext = false;
14320         this.hasFocus = true;
14321         
14322         if(this.triggerAction == 'all') {
14323             this.doQuery(this.allQuery, true);
14324         } else {
14325             this.doQuery(this.getRawValue());
14326         }
14327     },
14328     
14329     listKeyPress : function(e)
14330     {
14331         //Roo.log('listkeypress');
14332         // scroll to first matching element based on key pres..
14333         if (e.isSpecialKey()) {
14334             return false;
14335         }
14336         var k = String.fromCharCode(e.getKey()).toUpperCase();
14337         //Roo.log(k);
14338         var match  = false;
14339         var csel = this.view.getSelectedNodes();
14340         var cselitem = false;
14341         if (csel.length) {
14342             var ix = this.view.indexOf(csel[0]);
14343             cselitem  = this.store.getAt(ix);
14344             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14345                 cselitem = false;
14346             }
14347             
14348         }
14349         
14350         this.store.each(function(v) { 
14351             if (cselitem) {
14352                 // start at existing selection.
14353                 if (cselitem.id == v.id) {
14354                     cselitem = false;
14355                 }
14356                 return true;
14357             }
14358                 
14359             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14360                 match = this.store.indexOf(v);
14361                 return false;
14362             }
14363             return true;
14364         }, this);
14365         
14366         if (match === false) {
14367             return true; // no more action?
14368         }
14369         // scroll to?
14370         this.view.select(match);
14371         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14372         sn.scrollIntoView(sn.dom.parentNode, false);
14373     },
14374     
14375     onViewScroll : function(e, t){
14376         
14377         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){
14378             return;
14379         }
14380         
14381         this.hasQuery = true;
14382         
14383         this.loading = this.list.select('.loading', true).first();
14384         
14385         if(this.loading === null){
14386             this.list.createChild({
14387                 tag: 'div',
14388                 cls: 'loading roo-select2-more-results roo-select2-active',
14389                 html: 'Loading more results...'
14390             });
14391             
14392             this.loading = this.list.select('.loading', true).first();
14393             
14394             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14395             
14396             this.loading.hide();
14397         }
14398         
14399         this.loading.show();
14400         
14401         var _combo = this;
14402         
14403         this.page++;
14404         this.loadNext = true;
14405         
14406         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14407         
14408         return;
14409     },
14410     
14411     addItem : function(o)
14412     {   
14413         var dv = ''; // display value
14414         
14415         if (this.displayField) {
14416             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14417         } else {
14418             // this is an error condition!!!
14419             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14420         }
14421         
14422         if(!dv.length){
14423             return;
14424         }
14425         
14426         var choice = this.choices.createChild({
14427             tag: 'li',
14428             cls: 'roo-select2-search-choice',
14429             cn: [
14430                 {
14431                     tag: 'div',
14432                     html: dv
14433                 },
14434                 {
14435                     tag: 'a',
14436                     href: '#',
14437                     cls: 'roo-select2-search-choice-close fa fa-times',
14438                     tabindex: '-1'
14439                 }
14440             ]
14441             
14442         }, this.searchField);
14443         
14444         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14445         
14446         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14447         
14448         this.item.push(o);
14449         
14450         this.lastData = o;
14451         
14452         this.syncValue();
14453         
14454         this.inputEl().dom.value = '';
14455         
14456         this.validate();
14457     },
14458     
14459     onRemoveItem : function(e, _self, o)
14460     {
14461         e.preventDefault();
14462         
14463         this.lastItem = Roo.apply([], this.item);
14464         
14465         var index = this.item.indexOf(o.data) * 1;
14466         
14467         if( index < 0){
14468             Roo.log('not this item?!');
14469             return;
14470         }
14471         
14472         this.item.splice(index, 1);
14473         o.item.remove();
14474         
14475         this.syncValue();
14476         
14477         this.fireEvent('remove', this, e);
14478         
14479         this.validate();
14480         
14481     },
14482     
14483     syncValue : function()
14484     {
14485         if(!this.item.length){
14486             this.clearValue();
14487             return;
14488         }
14489             
14490         var value = [];
14491         var _this = this;
14492         Roo.each(this.item, function(i){
14493             if(_this.valueField){
14494                 value.push(i[_this.valueField]);
14495                 return;
14496             }
14497
14498             value.push(i);
14499         });
14500
14501         this.value = value.join(',');
14502
14503         if(this.hiddenField){
14504             this.hiddenField.dom.value = this.value;
14505         }
14506         
14507         this.store.fireEvent("datachanged", this.store);
14508         
14509         this.validate();
14510     },
14511     
14512     clearItem : function()
14513     {
14514         if(!this.multiple){
14515             return;
14516         }
14517         
14518         this.item = [];
14519         
14520         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14521            c.remove();
14522         });
14523         
14524         this.syncValue();
14525         
14526         this.validate();
14527         
14528         if(this.tickable && !Roo.isTouch){
14529             this.view.refresh();
14530         }
14531     },
14532     
14533     inputEl: function ()
14534     {
14535         if(Roo.isIOS && this.useNativeIOS){
14536             return this.el.select('select.roo-ios-select', true).first();
14537         }
14538         
14539         if(Roo.isTouch && this.mobileTouchView){
14540             return this.el.select('input.form-control',true).first();
14541         }
14542         
14543         if(this.tickable){
14544             return this.searchField;
14545         }
14546         
14547         return this.el.select('input.form-control',true).first();
14548     },
14549     
14550     onTickableFooterButtonClick : function(e, btn, el)
14551     {
14552         e.preventDefault();
14553         
14554         this.lastItem = Roo.apply([], this.item);
14555         
14556         if(btn && btn.name == 'cancel'){
14557             this.tickItems = Roo.apply([], this.item);
14558             this.collapse();
14559             return;
14560         }
14561         
14562         this.clearItem();
14563         
14564         var _this = this;
14565         
14566         Roo.each(this.tickItems, function(o){
14567             _this.addItem(o);
14568         });
14569         
14570         this.collapse();
14571         
14572     },
14573     
14574     validate : function()
14575     {
14576         var v = this.getRawValue();
14577         
14578         if(this.multiple){
14579             v = this.getValue();
14580         }
14581         
14582         if(this.disabled || this.allowBlank || v.length){
14583             this.markValid();
14584             return true;
14585         }
14586         
14587         this.markInvalid();
14588         return false;
14589     },
14590     
14591     tickableInputEl : function()
14592     {
14593         if(!this.tickable || !this.editable){
14594             return this.inputEl();
14595         }
14596         
14597         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14598     },
14599     
14600     
14601     getAutoCreateTouchView : function()
14602     {
14603         var id = Roo.id();
14604         
14605         var cfg = {
14606             cls: 'form-group' //input-group
14607         };
14608         
14609         var input =  {
14610             tag: 'input',
14611             id : id,
14612             type : this.inputType,
14613             cls : 'form-control x-combo-noedit',
14614             autocomplete: 'new-password',
14615             placeholder : this.placeholder || '',
14616             readonly : true
14617         };
14618         
14619         if (this.name) {
14620             input.name = this.name;
14621         }
14622         
14623         if (this.size) {
14624             input.cls += ' input-' + this.size;
14625         }
14626         
14627         if (this.disabled) {
14628             input.disabled = true;
14629         }
14630         
14631         var inputblock = {
14632             cls : '',
14633             cn : [
14634                 input
14635             ]
14636         };
14637         
14638         if(this.before){
14639             inputblock.cls += ' input-group';
14640             
14641             inputblock.cn.unshift({
14642                 tag :'span',
14643                 cls : 'input-group-addon',
14644                 html : this.before
14645             });
14646         }
14647         
14648         if(this.removable && !this.multiple){
14649             inputblock.cls += ' roo-removable';
14650             
14651             inputblock.cn.push({
14652                 tag: 'button',
14653                 html : 'x',
14654                 cls : 'roo-combo-removable-btn close'
14655             });
14656         }
14657
14658         if(this.hasFeedback && !this.allowBlank){
14659             
14660             inputblock.cls += ' has-feedback';
14661             
14662             inputblock.cn.push({
14663                 tag: 'span',
14664                 cls: 'glyphicon form-control-feedback'
14665             });
14666             
14667         }
14668         
14669         if (this.after) {
14670             
14671             inputblock.cls += (this.before) ? '' : ' input-group';
14672             
14673             inputblock.cn.push({
14674                 tag :'span',
14675                 cls : 'input-group-addon',
14676                 html : this.after
14677             });
14678         }
14679
14680         var box = {
14681             tag: 'div',
14682             cn: [
14683                 {
14684                     tag: 'input',
14685                     type : 'hidden',
14686                     cls: 'form-hidden-field'
14687                 },
14688                 inputblock
14689             ]
14690             
14691         };
14692         
14693         if(this.multiple){
14694             box = {
14695                 tag: 'div',
14696                 cn: [
14697                     {
14698                         tag: 'input',
14699                         type : 'hidden',
14700                         cls: 'form-hidden-field'
14701                     },
14702                     {
14703                         tag: 'ul',
14704                         cls: 'roo-select2-choices',
14705                         cn:[
14706                             {
14707                                 tag: 'li',
14708                                 cls: 'roo-select2-search-field',
14709                                 cn: [
14710
14711                                     inputblock
14712                                 ]
14713                             }
14714                         ]
14715                     }
14716                 ]
14717             }
14718         };
14719         
14720         var combobox = {
14721             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14722             cn: [
14723                 box
14724             ]
14725         };
14726         
14727         if(!this.multiple && this.showToggleBtn){
14728             
14729             var caret = {
14730                         tag: 'span',
14731                         cls: 'caret'
14732             };
14733             
14734             if (this.caret != false) {
14735                 caret = {
14736                      tag: 'i',
14737                      cls: 'fa fa-' + this.caret
14738                 };
14739                 
14740             }
14741             
14742             combobox.cn.push({
14743                 tag :'span',
14744                 cls : 'input-group-addon btn dropdown-toggle',
14745                 cn : [
14746                     caret,
14747                     {
14748                         tag: 'span',
14749                         cls: 'combobox-clear',
14750                         cn  : [
14751                             {
14752                                 tag : 'i',
14753                                 cls: 'icon-remove'
14754                             }
14755                         ]
14756                     }
14757                 ]
14758
14759             })
14760         }
14761         
14762         if(this.multiple){
14763             combobox.cls += ' roo-select2-container-multi';
14764         }
14765         
14766         var align = this.labelAlign || this.parentLabelAlign();
14767         
14768         if (align ==='left' && this.fieldLabel.length) {
14769
14770             cfg.cn = [
14771                 {
14772                    tag : 'i',
14773                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14774                    tooltip : 'This field is required'
14775                 },
14776                 {
14777                     tag: 'label',
14778                     cls : 'control-label',
14779                     html : this.fieldLabel
14780
14781                 },
14782                 {
14783                     cls : '', 
14784                     cn: [
14785                         combobox
14786                     ]
14787                 }
14788             ];
14789             
14790             var labelCfg = cfg.cn[1];
14791             var contentCfg = cfg.cn[2];
14792             
14793
14794             if(this.indicatorpos == 'right'){
14795                 cfg.cn = [
14796                     {
14797                         tag: 'label',
14798                         'for' :  id,
14799                         cls : 'control-label',
14800                         cn : [
14801                             {
14802                                 tag : 'span',
14803                                 html : this.fieldLabel
14804                             },
14805                             {
14806                                 tag : 'i',
14807                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14808                                 tooltip : 'This field is required'
14809                             }
14810                         ]
14811                     },
14812                     {
14813                         cls : "",
14814                         cn: [
14815                             combobox
14816                         ]
14817                     }
14818
14819                 ];
14820                 
14821                 labelCfg = cfg.cn[0];
14822                 contentCfg = cfg.cn[1];
14823             }
14824             
14825            
14826             
14827             if(this.labelWidth > 12){
14828                 labelCfg.style = "width: " + this.labelWidth + 'px';
14829             }
14830             
14831             if(this.labelWidth < 13 && this.labelmd == 0){
14832                 this.labelmd = this.labelWidth;
14833             }
14834             
14835             if(this.labellg > 0){
14836                 labelCfg.cls += ' col-lg-' + this.labellg;
14837                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14838             }
14839             
14840             if(this.labelmd > 0){
14841                 labelCfg.cls += ' col-md-' + this.labelmd;
14842                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14843             }
14844             
14845             if(this.labelsm > 0){
14846                 labelCfg.cls += ' col-sm-' + this.labelsm;
14847                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14848             }
14849             
14850             if(this.labelxs > 0){
14851                 labelCfg.cls += ' col-xs-' + this.labelxs;
14852                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14853             }
14854                 
14855                 
14856         } else if ( this.fieldLabel.length) {
14857             cfg.cn = [
14858                 {
14859                    tag : 'i',
14860                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14861                    tooltip : 'This field is required'
14862                 },
14863                 {
14864                     tag: 'label',
14865                     cls : 'control-label',
14866                     html : this.fieldLabel
14867
14868                 },
14869                 {
14870                     cls : '', 
14871                     cn: [
14872                         combobox
14873                     ]
14874                 }
14875             ];
14876             
14877             if(this.indicatorpos == 'right'){
14878                 cfg.cn = [
14879                     {
14880                         tag: 'label',
14881                         cls : 'control-label',
14882                         html : this.fieldLabel,
14883                         cn : [
14884                             {
14885                                tag : 'i',
14886                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14887                                tooltip : 'This field is required'
14888                             }
14889                         ]
14890                     },
14891                     {
14892                         cls : '', 
14893                         cn: [
14894                             combobox
14895                         ]
14896                     }
14897                 ];
14898             }
14899         } else {
14900             cfg.cn = combobox;    
14901         }
14902         
14903         
14904         var settings = this;
14905         
14906         ['xs','sm','md','lg'].map(function(size){
14907             if (settings[size]) {
14908                 cfg.cls += ' col-' + size + '-' + settings[size];
14909             }
14910         });
14911         
14912         return cfg;
14913     },
14914     
14915     initTouchView : function()
14916     {
14917         this.renderTouchView();
14918         
14919         this.touchViewEl.on('scroll', function(){
14920             this.el.dom.scrollTop = 0;
14921         }, this);
14922         
14923         this.originalValue = this.getValue();
14924         
14925         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14926         
14927         this.inputEl().on("click", this.showTouchView, this);
14928         if (this.triggerEl) {
14929             this.triggerEl.on("click", this.showTouchView, this);
14930         }
14931         
14932         
14933         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14934         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14935         
14936         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14937         
14938         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14939         this.store.on('load', this.onTouchViewLoad, this);
14940         this.store.on('loadexception', this.onTouchViewLoadException, this);
14941         
14942         if(this.hiddenName){
14943             
14944             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14945             
14946             this.hiddenField.dom.value =
14947                 this.hiddenValue !== undefined ? this.hiddenValue :
14948                 this.value !== undefined ? this.value : '';
14949         
14950             this.el.dom.removeAttribute('name');
14951             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14952         }
14953         
14954         if(this.multiple){
14955             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14956             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14957         }
14958         
14959         if(this.removable && !this.multiple){
14960             var close = this.closeTriggerEl();
14961             if(close){
14962                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14963                 close.on('click', this.removeBtnClick, this, close);
14964             }
14965         }
14966         /*
14967          * fix the bug in Safari iOS8
14968          */
14969         this.inputEl().on("focus", function(e){
14970             document.activeElement.blur();
14971         }, this);
14972         
14973         return;
14974         
14975         
14976     },
14977     
14978     renderTouchView : function()
14979     {
14980         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14981         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14982         
14983         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14984         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14985         
14986         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14987         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14988         this.touchViewBodyEl.setStyle('overflow', 'auto');
14989         
14990         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14991         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14992         
14993         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14994         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14995         
14996     },
14997     
14998     showTouchView : function()
14999     {
15000         if(this.disabled){
15001             return;
15002         }
15003         
15004         this.touchViewHeaderEl.hide();
15005
15006         if(this.modalTitle.length){
15007             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15008             this.touchViewHeaderEl.show();
15009         }
15010
15011         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15012         this.touchViewEl.show();
15013
15014         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15015         
15016         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15017         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15018
15019         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15020
15021         if(this.modalTitle.length){
15022             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15023         }
15024         
15025         this.touchViewBodyEl.setHeight(bodyHeight);
15026
15027         if(this.animate){
15028             var _this = this;
15029             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15030         }else{
15031             this.touchViewEl.addClass('in');
15032         }
15033
15034         this.doTouchViewQuery();
15035         
15036     },
15037     
15038     hideTouchView : function()
15039     {
15040         this.touchViewEl.removeClass('in');
15041
15042         if(this.animate){
15043             var _this = this;
15044             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15045         }else{
15046             this.touchViewEl.setStyle('display', 'none');
15047         }
15048         
15049     },
15050     
15051     setTouchViewValue : function()
15052     {
15053         if(this.multiple){
15054             this.clearItem();
15055         
15056             var _this = this;
15057
15058             Roo.each(this.tickItems, function(o){
15059                 this.addItem(o);
15060             }, this);
15061         }
15062         
15063         this.hideTouchView();
15064     },
15065     
15066     doTouchViewQuery : function()
15067     {
15068         var qe = {
15069             query: '',
15070             forceAll: true,
15071             combo: this,
15072             cancel:false
15073         };
15074         
15075         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15076             return false;
15077         }
15078         
15079         if(!this.alwaysQuery || this.mode == 'local'){
15080             this.onTouchViewLoad();
15081             return;
15082         }
15083         
15084         this.store.load();
15085     },
15086     
15087     onTouchViewBeforeLoad : function(combo,opts)
15088     {
15089         return;
15090     },
15091
15092     // private
15093     onTouchViewLoad : function()
15094     {
15095         if(this.store.getCount() < 1){
15096             this.onTouchViewEmptyResults();
15097             return;
15098         }
15099         
15100         this.clearTouchView();
15101         
15102         var rawValue = this.getRawValue();
15103         
15104         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15105         
15106         this.tickItems = [];
15107         
15108         this.store.data.each(function(d, rowIndex){
15109             var row = this.touchViewListGroup.createChild(template);
15110             
15111             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15112                 row.addClass(d.data.cls);
15113             }
15114             
15115             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15116                 var cfg = {
15117                     data : d.data,
15118                     html : d.data[this.displayField]
15119                 };
15120                 
15121                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15122                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15123                 }
15124             }
15125             row.removeClass('selected');
15126             if(!this.multiple && this.valueField &&
15127                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15128             {
15129                 // radio buttons..
15130                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15131                 row.addClass('selected');
15132             }
15133             
15134             if(this.multiple && this.valueField &&
15135                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15136             {
15137                 
15138                 // checkboxes...
15139                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15140                 this.tickItems.push(d.data);
15141             }
15142             
15143             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15144             
15145         }, this);
15146         
15147         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15148         
15149         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15150
15151         if(this.modalTitle.length){
15152             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15153         }
15154
15155         var listHeight = this.touchViewListGroup.getHeight();
15156         
15157         var _this = this;
15158         
15159         if(firstChecked && listHeight > bodyHeight){
15160             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15161         }
15162         
15163     },
15164     
15165     onTouchViewLoadException : function()
15166     {
15167         this.hideTouchView();
15168     },
15169     
15170     onTouchViewEmptyResults : function()
15171     {
15172         this.clearTouchView();
15173         
15174         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15175         
15176         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15177         
15178     },
15179     
15180     clearTouchView : function()
15181     {
15182         this.touchViewListGroup.dom.innerHTML = '';
15183     },
15184     
15185     onTouchViewClick : function(e, el, o)
15186     {
15187         e.preventDefault();
15188         
15189         var row = o.row;
15190         var rowIndex = o.rowIndex;
15191         
15192         var r = this.store.getAt(rowIndex);
15193         
15194         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15195             
15196             if(!this.multiple){
15197                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15198                     c.dom.removeAttribute('checked');
15199                 }, this);
15200
15201                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15202
15203                 this.setFromData(r.data);
15204
15205                 var close = this.closeTriggerEl();
15206
15207                 if(close){
15208                     close.show();
15209                 }
15210
15211                 this.hideTouchView();
15212
15213                 this.fireEvent('select', this, r, rowIndex);
15214
15215                 return;
15216             }
15217
15218             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15219                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15220                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15221                 return;
15222             }
15223
15224             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15225             this.addItem(r.data);
15226             this.tickItems.push(r.data);
15227         }
15228     },
15229     
15230     getAutoCreateNativeIOS : function()
15231     {
15232         var cfg = {
15233             cls: 'form-group' //input-group,
15234         };
15235         
15236         var combobox =  {
15237             tag: 'select',
15238             cls : 'roo-ios-select'
15239         };
15240         
15241         if (this.name) {
15242             combobox.name = this.name;
15243         }
15244         
15245         if (this.disabled) {
15246             combobox.disabled = true;
15247         }
15248         
15249         var settings = this;
15250         
15251         ['xs','sm','md','lg'].map(function(size){
15252             if (settings[size]) {
15253                 cfg.cls += ' col-' + size + '-' + settings[size];
15254             }
15255         });
15256         
15257         cfg.cn = combobox;
15258         
15259         return cfg;
15260         
15261     },
15262     
15263     initIOSView : function()
15264     {
15265         this.store.on('load', this.onIOSViewLoad, this);
15266         
15267         return;
15268     },
15269     
15270     onIOSViewLoad : function()
15271     {
15272         if(this.store.getCount() < 1){
15273             return;
15274         }
15275         
15276         this.clearIOSView();
15277         
15278         if(this.allowBlank) {
15279             
15280             var default_text = '-- SELECT --';
15281             
15282             if(this.placeholder.length){
15283                 default_text = this.placeholder;
15284             }
15285             
15286             if(this.emptyTitle.length){
15287                 default_text += ' - ' + this.emptyTitle + ' -';
15288             }
15289             
15290             var opt = this.inputEl().createChild({
15291                 tag: 'option',
15292                 value : 0,
15293                 html : default_text
15294             });
15295             
15296             var o = {};
15297             o[this.valueField] = 0;
15298             o[this.displayField] = default_text;
15299             
15300             this.ios_options.push({
15301                 data : o,
15302                 el : opt
15303             });
15304             
15305         }
15306         
15307         this.store.data.each(function(d, rowIndex){
15308             
15309             var html = '';
15310             
15311             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15312                 html = d.data[this.displayField];
15313             }
15314             
15315             var value = '';
15316             
15317             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15318                 value = d.data[this.valueField];
15319             }
15320             
15321             var option = {
15322                 tag: 'option',
15323                 value : value,
15324                 html : html
15325             };
15326             
15327             if(this.value == d.data[this.valueField]){
15328                 option['selected'] = true;
15329             }
15330             
15331             var opt = this.inputEl().createChild(option);
15332             
15333             this.ios_options.push({
15334                 data : d.data,
15335                 el : opt
15336             });
15337             
15338         }, this);
15339         
15340         this.inputEl().on('change', function(){
15341            this.fireEvent('select', this);
15342         }, this);
15343         
15344     },
15345     
15346     clearIOSView: function()
15347     {
15348         this.inputEl().dom.innerHTML = '';
15349         
15350         this.ios_options = [];
15351     },
15352     
15353     setIOSValue: function(v)
15354     {
15355         this.value = v;
15356         
15357         if(!this.ios_options){
15358             return;
15359         }
15360         
15361         Roo.each(this.ios_options, function(opts){
15362            
15363            opts.el.dom.removeAttribute('selected');
15364            
15365            if(opts.data[this.valueField] != v){
15366                return;
15367            }
15368            
15369            opts.el.dom.setAttribute('selected', true);
15370            
15371         }, this);
15372     }
15373
15374     /** 
15375     * @cfg {Boolean} grow 
15376     * @hide 
15377     */
15378     /** 
15379     * @cfg {Number} growMin 
15380     * @hide 
15381     */
15382     /** 
15383     * @cfg {Number} growMax 
15384     * @hide 
15385     */
15386     /**
15387      * @hide
15388      * @method autoSize
15389      */
15390 });
15391
15392 Roo.apply(Roo.bootstrap.ComboBox,  {
15393     
15394     header : {
15395         tag: 'div',
15396         cls: 'modal-header',
15397         cn: [
15398             {
15399                 tag: 'h4',
15400                 cls: 'modal-title'
15401             }
15402         ]
15403     },
15404     
15405     body : {
15406         tag: 'div',
15407         cls: 'modal-body',
15408         cn: [
15409             {
15410                 tag: 'ul',
15411                 cls: 'list-group'
15412             }
15413         ]
15414     },
15415     
15416     listItemRadio : {
15417         tag: 'li',
15418         cls: 'list-group-item',
15419         cn: [
15420             {
15421                 tag: 'span',
15422                 cls: 'roo-combobox-list-group-item-value'
15423             },
15424             {
15425                 tag: 'div',
15426                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15427                 cn: [
15428                     {
15429                         tag: 'input',
15430                         type: 'radio'
15431                     },
15432                     {
15433                         tag: 'label'
15434                     }
15435                 ]
15436             }
15437         ]
15438     },
15439     
15440     listItemCheckbox : {
15441         tag: 'li',
15442         cls: 'list-group-item',
15443         cn: [
15444             {
15445                 tag: 'span',
15446                 cls: 'roo-combobox-list-group-item-value'
15447             },
15448             {
15449                 tag: 'div',
15450                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15451                 cn: [
15452                     {
15453                         tag: 'input',
15454                         type: 'checkbox'
15455                     },
15456                     {
15457                         tag: 'label'
15458                     }
15459                 ]
15460             }
15461         ]
15462     },
15463     
15464     emptyResult : {
15465         tag: 'div',
15466         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15467     },
15468     
15469     footer : {
15470         tag: 'div',
15471         cls: 'modal-footer',
15472         cn: [
15473             {
15474                 tag: 'div',
15475                 cls: 'row',
15476                 cn: [
15477                     {
15478                         tag: 'div',
15479                         cls: 'col-xs-6 text-left',
15480                         cn: {
15481                             tag: 'button',
15482                             cls: 'btn btn-danger roo-touch-view-cancel',
15483                             html: 'Cancel'
15484                         }
15485                     },
15486                     {
15487                         tag: 'div',
15488                         cls: 'col-xs-6 text-right',
15489                         cn: {
15490                             tag: 'button',
15491                             cls: 'btn btn-success roo-touch-view-ok',
15492                             html: 'OK'
15493                         }
15494                     }
15495                 ]
15496             }
15497         ]
15498         
15499     }
15500 });
15501
15502 Roo.apply(Roo.bootstrap.ComboBox,  {
15503     
15504     touchViewTemplate : {
15505         tag: 'div',
15506         cls: 'modal fade roo-combobox-touch-view',
15507         cn: [
15508             {
15509                 tag: 'div',
15510                 cls: 'modal-dialog',
15511                 style : 'position:fixed', // we have to fix position....
15512                 cn: [
15513                     {
15514                         tag: 'div',
15515                         cls: 'modal-content',
15516                         cn: [
15517                             Roo.bootstrap.ComboBox.header,
15518                             Roo.bootstrap.ComboBox.body,
15519                             Roo.bootstrap.ComboBox.footer
15520                         ]
15521                     }
15522                 ]
15523             }
15524         ]
15525     }
15526 });/*
15527  * Based on:
15528  * Ext JS Library 1.1.1
15529  * Copyright(c) 2006-2007, Ext JS, LLC.
15530  *
15531  * Originally Released Under LGPL - original licence link has changed is not relivant.
15532  *
15533  * Fork - LGPL
15534  * <script type="text/javascript">
15535  */
15536
15537 /**
15538  * @class Roo.View
15539  * @extends Roo.util.Observable
15540  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15541  * This class also supports single and multi selection modes. <br>
15542  * Create a data model bound view:
15543  <pre><code>
15544  var store = new Roo.data.Store(...);
15545
15546  var view = new Roo.View({
15547     el : "my-element",
15548     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15549  
15550     singleSelect: true,
15551     selectedClass: "ydataview-selected",
15552     store: store
15553  });
15554
15555  // listen for node click?
15556  view.on("click", function(vw, index, node, e){
15557  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15558  });
15559
15560  // load XML data
15561  dataModel.load("foobar.xml");
15562  </code></pre>
15563  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15564  * <br><br>
15565  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15566  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15567  * 
15568  * Note: old style constructor is still suported (container, template, config)
15569  * 
15570  * @constructor
15571  * Create a new View
15572  * @param {Object} config The config object
15573  * 
15574  */
15575 Roo.View = function(config, depreciated_tpl, depreciated_config){
15576     
15577     this.parent = false;
15578     
15579     if (typeof(depreciated_tpl) == 'undefined') {
15580         // new way.. - universal constructor.
15581         Roo.apply(this, config);
15582         this.el  = Roo.get(this.el);
15583     } else {
15584         // old format..
15585         this.el  = Roo.get(config);
15586         this.tpl = depreciated_tpl;
15587         Roo.apply(this, depreciated_config);
15588     }
15589     this.wrapEl  = this.el.wrap().wrap();
15590     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15591     
15592     
15593     if(typeof(this.tpl) == "string"){
15594         this.tpl = new Roo.Template(this.tpl);
15595     } else {
15596         // support xtype ctors..
15597         this.tpl = new Roo.factory(this.tpl, Roo);
15598     }
15599     
15600     
15601     this.tpl.compile();
15602     
15603     /** @private */
15604     this.addEvents({
15605         /**
15606          * @event beforeclick
15607          * Fires before a click is processed. Returns false to cancel the default action.
15608          * @param {Roo.View} this
15609          * @param {Number} index The index of the target node
15610          * @param {HTMLElement} node The target node
15611          * @param {Roo.EventObject} e The raw event object
15612          */
15613             "beforeclick" : true,
15614         /**
15615          * @event click
15616          * Fires when a template node is clicked.
15617          * @param {Roo.View} this
15618          * @param {Number} index The index of the target node
15619          * @param {HTMLElement} node The target node
15620          * @param {Roo.EventObject} e The raw event object
15621          */
15622             "click" : true,
15623         /**
15624          * @event dblclick
15625          * Fires when a template node is double clicked.
15626          * @param {Roo.View} this
15627          * @param {Number} index The index of the target node
15628          * @param {HTMLElement} node The target node
15629          * @param {Roo.EventObject} e The raw event object
15630          */
15631             "dblclick" : true,
15632         /**
15633          * @event contextmenu
15634          * Fires when a template node is right clicked.
15635          * @param {Roo.View} this
15636          * @param {Number} index The index of the target node
15637          * @param {HTMLElement} node The target node
15638          * @param {Roo.EventObject} e The raw event object
15639          */
15640             "contextmenu" : true,
15641         /**
15642          * @event selectionchange
15643          * Fires when the selected nodes change.
15644          * @param {Roo.View} this
15645          * @param {Array} selections Array of the selected nodes
15646          */
15647             "selectionchange" : true,
15648     
15649         /**
15650          * @event beforeselect
15651          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15652          * @param {Roo.View} this
15653          * @param {HTMLElement} node The node to be selected
15654          * @param {Array} selections Array of currently selected nodes
15655          */
15656             "beforeselect" : true,
15657         /**
15658          * @event preparedata
15659          * Fires on every row to render, to allow you to change the data.
15660          * @param {Roo.View} this
15661          * @param {Object} data to be rendered (change this)
15662          */
15663           "preparedata" : true
15664           
15665           
15666         });
15667
15668
15669
15670     this.el.on({
15671         "click": this.onClick,
15672         "dblclick": this.onDblClick,
15673         "contextmenu": this.onContextMenu,
15674         scope:this
15675     });
15676
15677     this.selections = [];
15678     this.nodes = [];
15679     this.cmp = new Roo.CompositeElementLite([]);
15680     if(this.store){
15681         this.store = Roo.factory(this.store, Roo.data);
15682         this.setStore(this.store, true);
15683     }
15684     
15685     if ( this.footer && this.footer.xtype) {
15686            
15687          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15688         
15689         this.footer.dataSource = this.store;
15690         this.footer.container = fctr;
15691         this.footer = Roo.factory(this.footer, Roo);
15692         fctr.insertFirst(this.el);
15693         
15694         // this is a bit insane - as the paging toolbar seems to detach the el..
15695 //        dom.parentNode.parentNode.parentNode
15696          // they get detached?
15697     }
15698     
15699     
15700     Roo.View.superclass.constructor.call(this);
15701     
15702     
15703 };
15704
15705 Roo.extend(Roo.View, Roo.util.Observable, {
15706     
15707      /**
15708      * @cfg {Roo.data.Store} store Data store to load data from.
15709      */
15710     store : false,
15711     
15712     /**
15713      * @cfg {String|Roo.Element} el The container element.
15714      */
15715     el : '',
15716     
15717     /**
15718      * @cfg {String|Roo.Template} tpl The template used by this View 
15719      */
15720     tpl : false,
15721     /**
15722      * @cfg {String} dataName the named area of the template to use as the data area
15723      *                          Works with domtemplates roo-name="name"
15724      */
15725     dataName: false,
15726     /**
15727      * @cfg {String} selectedClass The css class to add to selected nodes
15728      */
15729     selectedClass : "x-view-selected",
15730      /**
15731      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15732      */
15733     emptyText : "",
15734     
15735     /**
15736      * @cfg {String} text to display on mask (default Loading)
15737      */
15738     mask : false,
15739     /**
15740      * @cfg {Boolean} multiSelect Allow multiple selection
15741      */
15742     multiSelect : false,
15743     /**
15744      * @cfg {Boolean} singleSelect Allow single selection
15745      */
15746     singleSelect:  false,
15747     
15748     /**
15749      * @cfg {Boolean} toggleSelect - selecting 
15750      */
15751     toggleSelect : false,
15752     
15753     /**
15754      * @cfg {Boolean} tickable - selecting 
15755      */
15756     tickable : false,
15757     
15758     /**
15759      * Returns the element this view is bound to.
15760      * @return {Roo.Element}
15761      */
15762     getEl : function(){
15763         return this.wrapEl;
15764     },
15765     
15766     
15767
15768     /**
15769      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15770      */
15771     refresh : function(){
15772         //Roo.log('refresh');
15773         var t = this.tpl;
15774         
15775         // if we are using something like 'domtemplate', then
15776         // the what gets used is:
15777         // t.applySubtemplate(NAME, data, wrapping data..)
15778         // the outer template then get' applied with
15779         //     the store 'extra data'
15780         // and the body get's added to the
15781         //      roo-name="data" node?
15782         //      <span class='roo-tpl-{name}'></span> ?????
15783         
15784         
15785         
15786         this.clearSelections();
15787         this.el.update("");
15788         var html = [];
15789         var records = this.store.getRange();
15790         if(records.length < 1) {
15791             
15792             // is this valid??  = should it render a template??
15793             
15794             this.el.update(this.emptyText);
15795             return;
15796         }
15797         var el = this.el;
15798         if (this.dataName) {
15799             this.el.update(t.apply(this.store.meta)); //????
15800             el = this.el.child('.roo-tpl-' + this.dataName);
15801         }
15802         
15803         for(var i = 0, len = records.length; i < len; i++){
15804             var data = this.prepareData(records[i].data, i, records[i]);
15805             this.fireEvent("preparedata", this, data, i, records[i]);
15806             
15807             var d = Roo.apply({}, data);
15808             
15809             if(this.tickable){
15810                 Roo.apply(d, {'roo-id' : Roo.id()});
15811                 
15812                 var _this = this;
15813             
15814                 Roo.each(this.parent.item, function(item){
15815                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15816                         return;
15817                     }
15818                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15819                 });
15820             }
15821             
15822             html[html.length] = Roo.util.Format.trim(
15823                 this.dataName ?
15824                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15825                     t.apply(d)
15826             );
15827         }
15828         
15829         
15830         
15831         el.update(html.join(""));
15832         this.nodes = el.dom.childNodes;
15833         this.updateIndexes(0);
15834     },
15835     
15836
15837     /**
15838      * Function to override to reformat the data that is sent to
15839      * the template for each node.
15840      * DEPRICATED - use the preparedata event handler.
15841      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15842      * a JSON object for an UpdateManager bound view).
15843      */
15844     prepareData : function(data, index, record)
15845     {
15846         this.fireEvent("preparedata", this, data, index, record);
15847         return data;
15848     },
15849
15850     onUpdate : function(ds, record){
15851         // Roo.log('on update');   
15852         this.clearSelections();
15853         var index = this.store.indexOf(record);
15854         var n = this.nodes[index];
15855         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15856         n.parentNode.removeChild(n);
15857         this.updateIndexes(index, index);
15858     },
15859
15860     
15861     
15862 // --------- FIXME     
15863     onAdd : function(ds, records, index)
15864     {
15865         //Roo.log(['on Add', ds, records, index] );        
15866         this.clearSelections();
15867         if(this.nodes.length == 0){
15868             this.refresh();
15869             return;
15870         }
15871         var n = this.nodes[index];
15872         for(var i = 0, len = records.length; i < len; i++){
15873             var d = this.prepareData(records[i].data, i, records[i]);
15874             if(n){
15875                 this.tpl.insertBefore(n, d);
15876             }else{
15877                 
15878                 this.tpl.append(this.el, d);
15879             }
15880         }
15881         this.updateIndexes(index);
15882     },
15883
15884     onRemove : function(ds, record, index){
15885        // Roo.log('onRemove');
15886         this.clearSelections();
15887         var el = this.dataName  ?
15888             this.el.child('.roo-tpl-' + this.dataName) :
15889             this.el; 
15890         
15891         el.dom.removeChild(this.nodes[index]);
15892         this.updateIndexes(index);
15893     },
15894
15895     /**
15896      * Refresh an individual node.
15897      * @param {Number} index
15898      */
15899     refreshNode : function(index){
15900         this.onUpdate(this.store, this.store.getAt(index));
15901     },
15902
15903     updateIndexes : function(startIndex, endIndex){
15904         var ns = this.nodes;
15905         startIndex = startIndex || 0;
15906         endIndex = endIndex || ns.length - 1;
15907         for(var i = startIndex; i <= endIndex; i++){
15908             ns[i].nodeIndex = i;
15909         }
15910     },
15911
15912     /**
15913      * Changes the data store this view uses and refresh the view.
15914      * @param {Store} store
15915      */
15916     setStore : function(store, initial){
15917         if(!initial && this.store){
15918             this.store.un("datachanged", this.refresh);
15919             this.store.un("add", this.onAdd);
15920             this.store.un("remove", this.onRemove);
15921             this.store.un("update", this.onUpdate);
15922             this.store.un("clear", this.refresh);
15923             this.store.un("beforeload", this.onBeforeLoad);
15924             this.store.un("load", this.onLoad);
15925             this.store.un("loadexception", this.onLoad);
15926         }
15927         if(store){
15928           
15929             store.on("datachanged", this.refresh, this);
15930             store.on("add", this.onAdd, this);
15931             store.on("remove", this.onRemove, this);
15932             store.on("update", this.onUpdate, this);
15933             store.on("clear", this.refresh, this);
15934             store.on("beforeload", this.onBeforeLoad, this);
15935             store.on("load", this.onLoad, this);
15936             store.on("loadexception", this.onLoad, this);
15937         }
15938         
15939         if(store){
15940             this.refresh();
15941         }
15942     },
15943     /**
15944      * onbeforeLoad - masks the loading area.
15945      *
15946      */
15947     onBeforeLoad : function(store,opts)
15948     {
15949          //Roo.log('onBeforeLoad');   
15950         if (!opts.add) {
15951             this.el.update("");
15952         }
15953         this.el.mask(this.mask ? this.mask : "Loading" ); 
15954     },
15955     onLoad : function ()
15956     {
15957         this.el.unmask();
15958     },
15959     
15960
15961     /**
15962      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15963      * @param {HTMLElement} node
15964      * @return {HTMLElement} The template node
15965      */
15966     findItemFromChild : function(node){
15967         var el = this.dataName  ?
15968             this.el.child('.roo-tpl-' + this.dataName,true) :
15969             this.el.dom; 
15970         
15971         if(!node || node.parentNode == el){
15972                     return node;
15973             }
15974             var p = node.parentNode;
15975             while(p && p != el){
15976             if(p.parentNode == el){
15977                 return p;
15978             }
15979             p = p.parentNode;
15980         }
15981             return null;
15982     },
15983
15984     /** @ignore */
15985     onClick : function(e){
15986         var item = this.findItemFromChild(e.getTarget());
15987         if(item){
15988             var index = this.indexOf(item);
15989             if(this.onItemClick(item, index, e) !== false){
15990                 this.fireEvent("click", this, index, item, e);
15991             }
15992         }else{
15993             this.clearSelections();
15994         }
15995     },
15996
15997     /** @ignore */
15998     onContextMenu : function(e){
15999         var item = this.findItemFromChild(e.getTarget());
16000         if(item){
16001             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16002         }
16003     },
16004
16005     /** @ignore */
16006     onDblClick : function(e){
16007         var item = this.findItemFromChild(e.getTarget());
16008         if(item){
16009             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16010         }
16011     },
16012
16013     onItemClick : function(item, index, e)
16014     {
16015         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16016             return false;
16017         }
16018         if (this.toggleSelect) {
16019             var m = this.isSelected(item) ? 'unselect' : 'select';
16020             //Roo.log(m);
16021             var _t = this;
16022             _t[m](item, true, false);
16023             return true;
16024         }
16025         if(this.multiSelect || this.singleSelect){
16026             if(this.multiSelect && e.shiftKey && this.lastSelection){
16027                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16028             }else{
16029                 this.select(item, this.multiSelect && e.ctrlKey);
16030                 this.lastSelection = item;
16031             }
16032             
16033             if(!this.tickable){
16034                 e.preventDefault();
16035             }
16036             
16037         }
16038         return true;
16039     },
16040
16041     /**
16042      * Get the number of selected nodes.
16043      * @return {Number}
16044      */
16045     getSelectionCount : function(){
16046         return this.selections.length;
16047     },
16048
16049     /**
16050      * Get the currently selected nodes.
16051      * @return {Array} An array of HTMLElements
16052      */
16053     getSelectedNodes : function(){
16054         return this.selections;
16055     },
16056
16057     /**
16058      * Get the indexes of the selected nodes.
16059      * @return {Array}
16060      */
16061     getSelectedIndexes : function(){
16062         var indexes = [], s = this.selections;
16063         for(var i = 0, len = s.length; i < len; i++){
16064             indexes.push(s[i].nodeIndex);
16065         }
16066         return indexes;
16067     },
16068
16069     /**
16070      * Clear all selections
16071      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16072      */
16073     clearSelections : function(suppressEvent){
16074         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16075             this.cmp.elements = this.selections;
16076             this.cmp.removeClass(this.selectedClass);
16077             this.selections = [];
16078             if(!suppressEvent){
16079                 this.fireEvent("selectionchange", this, this.selections);
16080             }
16081         }
16082     },
16083
16084     /**
16085      * Returns true if the passed node is selected
16086      * @param {HTMLElement/Number} node The node or node index
16087      * @return {Boolean}
16088      */
16089     isSelected : function(node){
16090         var s = this.selections;
16091         if(s.length < 1){
16092             return false;
16093         }
16094         node = this.getNode(node);
16095         return s.indexOf(node) !== -1;
16096     },
16097
16098     /**
16099      * Selects nodes.
16100      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16101      * @param {Boolean} keepExisting (optional) true to keep existing selections
16102      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16103      */
16104     select : function(nodeInfo, keepExisting, suppressEvent){
16105         if(nodeInfo instanceof Array){
16106             if(!keepExisting){
16107                 this.clearSelections(true);
16108             }
16109             for(var i = 0, len = nodeInfo.length; i < len; i++){
16110                 this.select(nodeInfo[i], true, true);
16111             }
16112             return;
16113         } 
16114         var node = this.getNode(nodeInfo);
16115         if(!node || this.isSelected(node)){
16116             return; // already selected.
16117         }
16118         if(!keepExisting){
16119             this.clearSelections(true);
16120         }
16121         
16122         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16123             Roo.fly(node).addClass(this.selectedClass);
16124             this.selections.push(node);
16125             if(!suppressEvent){
16126                 this.fireEvent("selectionchange", this, this.selections);
16127             }
16128         }
16129         
16130         
16131     },
16132       /**
16133      * Unselects nodes.
16134      * @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
16135      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16136      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16137      */
16138     unselect : function(nodeInfo, keepExisting, suppressEvent)
16139     {
16140         if(nodeInfo instanceof Array){
16141             Roo.each(this.selections, function(s) {
16142                 this.unselect(s, nodeInfo);
16143             }, this);
16144             return;
16145         }
16146         var node = this.getNode(nodeInfo);
16147         if(!node || !this.isSelected(node)){
16148             //Roo.log("not selected");
16149             return; // not selected.
16150         }
16151         // fireevent???
16152         var ns = [];
16153         Roo.each(this.selections, function(s) {
16154             if (s == node ) {
16155                 Roo.fly(node).removeClass(this.selectedClass);
16156
16157                 return;
16158             }
16159             ns.push(s);
16160         },this);
16161         
16162         this.selections= ns;
16163         this.fireEvent("selectionchange", this, this.selections);
16164     },
16165
16166     /**
16167      * Gets a template node.
16168      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16169      * @return {HTMLElement} The node or null if it wasn't found
16170      */
16171     getNode : function(nodeInfo){
16172         if(typeof nodeInfo == "string"){
16173             return document.getElementById(nodeInfo);
16174         }else if(typeof nodeInfo == "number"){
16175             return this.nodes[nodeInfo];
16176         }
16177         return nodeInfo;
16178     },
16179
16180     /**
16181      * Gets a range template nodes.
16182      * @param {Number} startIndex
16183      * @param {Number} endIndex
16184      * @return {Array} An array of nodes
16185      */
16186     getNodes : function(start, end){
16187         var ns = this.nodes;
16188         start = start || 0;
16189         end = typeof end == "undefined" ? ns.length - 1 : end;
16190         var nodes = [];
16191         if(start <= end){
16192             for(var i = start; i <= end; i++){
16193                 nodes.push(ns[i]);
16194             }
16195         } else{
16196             for(var i = start; i >= end; i--){
16197                 nodes.push(ns[i]);
16198             }
16199         }
16200         return nodes;
16201     },
16202
16203     /**
16204      * Finds the index of the passed node
16205      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16206      * @return {Number} The index of the node or -1
16207      */
16208     indexOf : function(node){
16209         node = this.getNode(node);
16210         if(typeof node.nodeIndex == "number"){
16211             return node.nodeIndex;
16212         }
16213         var ns = this.nodes;
16214         for(var i = 0, len = ns.length; i < len; i++){
16215             if(ns[i] == node){
16216                 return i;
16217             }
16218         }
16219         return -1;
16220     }
16221 });
16222 /*
16223  * - LGPL
16224  *
16225  * based on jquery fullcalendar
16226  * 
16227  */
16228
16229 Roo.bootstrap = Roo.bootstrap || {};
16230 /**
16231  * @class Roo.bootstrap.Calendar
16232  * @extends Roo.bootstrap.Component
16233  * Bootstrap Calendar class
16234  * @cfg {Boolean} loadMask (true|false) default false
16235  * @cfg {Object} header generate the user specific header of the calendar, default false
16236
16237  * @constructor
16238  * Create a new Container
16239  * @param {Object} config The config object
16240  */
16241
16242
16243
16244 Roo.bootstrap.Calendar = function(config){
16245     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16246      this.addEvents({
16247         /**
16248              * @event select
16249              * Fires when a date is selected
16250              * @param {DatePicker} this
16251              * @param {Date} date The selected date
16252              */
16253         'select': true,
16254         /**
16255              * @event monthchange
16256              * Fires when the displayed month changes 
16257              * @param {DatePicker} this
16258              * @param {Date} date The selected month
16259              */
16260         'monthchange': true,
16261         /**
16262              * @event evententer
16263              * Fires when mouse over an event
16264              * @param {Calendar} this
16265              * @param {event} Event
16266              */
16267         'evententer': true,
16268         /**
16269              * @event eventleave
16270              * Fires when the mouse leaves an
16271              * @param {Calendar} this
16272              * @param {event}
16273              */
16274         'eventleave': true,
16275         /**
16276              * @event eventclick
16277              * Fires when the mouse click an
16278              * @param {Calendar} this
16279              * @param {event}
16280              */
16281         'eventclick': true
16282         
16283     });
16284
16285 };
16286
16287 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16288     
16289      /**
16290      * @cfg {Number} startDay
16291      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16292      */
16293     startDay : 0,
16294     
16295     loadMask : false,
16296     
16297     header : false,
16298       
16299     getAutoCreate : function(){
16300         
16301         
16302         var fc_button = function(name, corner, style, content ) {
16303             return Roo.apply({},{
16304                 tag : 'span',
16305                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16306                          (corner.length ?
16307                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16308                             ''
16309                         ),
16310                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16311                 unselectable: 'on'
16312             });
16313         };
16314         
16315         var header = {};
16316         
16317         if(!this.header){
16318             header = {
16319                 tag : 'table',
16320                 cls : 'fc-header',
16321                 style : 'width:100%',
16322                 cn : [
16323                     {
16324                         tag: 'tr',
16325                         cn : [
16326                             {
16327                                 tag : 'td',
16328                                 cls : 'fc-header-left',
16329                                 cn : [
16330                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16331                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16332                                     { tag: 'span', cls: 'fc-header-space' },
16333                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16334
16335
16336                                 ]
16337                             },
16338
16339                             {
16340                                 tag : 'td',
16341                                 cls : 'fc-header-center',
16342                                 cn : [
16343                                     {
16344                                         tag: 'span',
16345                                         cls: 'fc-header-title',
16346                                         cn : {
16347                                             tag: 'H2',
16348                                             html : 'month / year'
16349                                         }
16350                                     }
16351
16352                                 ]
16353                             },
16354                             {
16355                                 tag : 'td',
16356                                 cls : 'fc-header-right',
16357                                 cn : [
16358                               /*      fc_button('month', 'left', '', 'month' ),
16359                                     fc_button('week', '', '', 'week' ),
16360                                     fc_button('day', 'right', '', 'day' )
16361                                 */    
16362
16363                                 ]
16364                             }
16365
16366                         ]
16367                     }
16368                 ]
16369             };
16370         }
16371         
16372         header = this.header;
16373         
16374        
16375         var cal_heads = function() {
16376             var ret = [];
16377             // fixme - handle this.
16378             
16379             for (var i =0; i < Date.dayNames.length; i++) {
16380                 var d = Date.dayNames[i];
16381                 ret.push({
16382                     tag: 'th',
16383                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16384                     html : d.substring(0,3)
16385                 });
16386                 
16387             }
16388             ret[0].cls += ' fc-first';
16389             ret[6].cls += ' fc-last';
16390             return ret;
16391         };
16392         var cal_cell = function(n) {
16393             return  {
16394                 tag: 'td',
16395                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16396                 cn : [
16397                     {
16398                         cn : [
16399                             {
16400                                 cls: 'fc-day-number',
16401                                 html: 'D'
16402                             },
16403                             {
16404                                 cls: 'fc-day-content',
16405                              
16406                                 cn : [
16407                                      {
16408                                         style: 'position: relative;' // height: 17px;
16409                                     }
16410                                 ]
16411                             }
16412                             
16413                             
16414                         ]
16415                     }
16416                 ]
16417                 
16418             }
16419         };
16420         var cal_rows = function() {
16421             
16422             var ret = [];
16423             for (var r = 0; r < 6; r++) {
16424                 var row= {
16425                     tag : 'tr',
16426                     cls : 'fc-week',
16427                     cn : []
16428                 };
16429                 
16430                 for (var i =0; i < Date.dayNames.length; i++) {
16431                     var d = Date.dayNames[i];
16432                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16433
16434                 }
16435                 row.cn[0].cls+=' fc-first';
16436                 row.cn[0].cn[0].style = 'min-height:90px';
16437                 row.cn[6].cls+=' fc-last';
16438                 ret.push(row);
16439                 
16440             }
16441             ret[0].cls += ' fc-first';
16442             ret[4].cls += ' fc-prev-last';
16443             ret[5].cls += ' fc-last';
16444             return ret;
16445             
16446         };
16447         
16448         var cal_table = {
16449             tag: 'table',
16450             cls: 'fc-border-separate',
16451             style : 'width:100%',
16452             cellspacing  : 0,
16453             cn : [
16454                 { 
16455                     tag: 'thead',
16456                     cn : [
16457                         { 
16458                             tag: 'tr',
16459                             cls : 'fc-first fc-last',
16460                             cn : cal_heads()
16461                         }
16462                     ]
16463                 },
16464                 { 
16465                     tag: 'tbody',
16466                     cn : cal_rows()
16467                 }
16468                   
16469             ]
16470         };
16471          
16472          var cfg = {
16473             cls : 'fc fc-ltr',
16474             cn : [
16475                 header,
16476                 {
16477                     cls : 'fc-content',
16478                     style : "position: relative;",
16479                     cn : [
16480                         {
16481                             cls : 'fc-view fc-view-month fc-grid',
16482                             style : 'position: relative',
16483                             unselectable : 'on',
16484                             cn : [
16485                                 {
16486                                     cls : 'fc-event-container',
16487                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16488                                 },
16489                                 cal_table
16490                             ]
16491                         }
16492                     ]
16493     
16494                 }
16495            ] 
16496             
16497         };
16498         
16499          
16500         
16501         return cfg;
16502     },
16503     
16504     
16505     initEvents : function()
16506     {
16507         if(!this.store){
16508             throw "can not find store for calendar";
16509         }
16510         
16511         var mark = {
16512             tag: "div",
16513             cls:"x-dlg-mask",
16514             style: "text-align:center",
16515             cn: [
16516                 {
16517                     tag: "div",
16518                     style: "background-color:white;width:50%;margin:250 auto",
16519                     cn: [
16520                         {
16521                             tag: "img",
16522                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16523                         },
16524                         {
16525                             tag: "span",
16526                             html: "Loading"
16527                         }
16528                         
16529                     ]
16530                 }
16531             ]
16532         };
16533         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16534         
16535         var size = this.el.select('.fc-content', true).first().getSize();
16536         this.maskEl.setSize(size.width, size.height);
16537         this.maskEl.enableDisplayMode("block");
16538         if(!this.loadMask){
16539             this.maskEl.hide();
16540         }
16541         
16542         this.store = Roo.factory(this.store, Roo.data);
16543         this.store.on('load', this.onLoad, this);
16544         this.store.on('beforeload', this.onBeforeLoad, this);
16545         
16546         this.resize();
16547         
16548         this.cells = this.el.select('.fc-day',true);
16549         //Roo.log(this.cells);
16550         this.textNodes = this.el.query('.fc-day-number');
16551         this.cells.addClassOnOver('fc-state-hover');
16552         
16553         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16554         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16555         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16556         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16557         
16558         this.on('monthchange', this.onMonthChange, this);
16559         
16560         this.update(new Date().clearTime());
16561     },
16562     
16563     resize : function() {
16564         var sz  = this.el.getSize();
16565         
16566         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16567         this.el.select('.fc-day-content div',true).setHeight(34);
16568     },
16569     
16570     
16571     // private
16572     showPrevMonth : function(e){
16573         this.update(this.activeDate.add("mo", -1));
16574     },
16575     showToday : function(e){
16576         this.update(new Date().clearTime());
16577     },
16578     // private
16579     showNextMonth : function(e){
16580         this.update(this.activeDate.add("mo", 1));
16581     },
16582
16583     // private
16584     showPrevYear : function(){
16585         this.update(this.activeDate.add("y", -1));
16586     },
16587
16588     // private
16589     showNextYear : function(){
16590         this.update(this.activeDate.add("y", 1));
16591     },
16592
16593     
16594    // private
16595     update : function(date)
16596     {
16597         var vd = this.activeDate;
16598         this.activeDate = date;
16599 //        if(vd && this.el){
16600 //            var t = date.getTime();
16601 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16602 //                Roo.log('using add remove');
16603 //                
16604 //                this.fireEvent('monthchange', this, date);
16605 //                
16606 //                this.cells.removeClass("fc-state-highlight");
16607 //                this.cells.each(function(c){
16608 //                   if(c.dateValue == t){
16609 //                       c.addClass("fc-state-highlight");
16610 //                       setTimeout(function(){
16611 //                            try{c.dom.firstChild.focus();}catch(e){}
16612 //                       }, 50);
16613 //                       return false;
16614 //                   }
16615 //                   return true;
16616 //                });
16617 //                return;
16618 //            }
16619 //        }
16620         
16621         var days = date.getDaysInMonth();
16622         
16623         var firstOfMonth = date.getFirstDateOfMonth();
16624         var startingPos = firstOfMonth.getDay()-this.startDay;
16625         
16626         if(startingPos < this.startDay){
16627             startingPos += 7;
16628         }
16629         
16630         var pm = date.add(Date.MONTH, -1);
16631         var prevStart = pm.getDaysInMonth()-startingPos;
16632 //        
16633         this.cells = this.el.select('.fc-day',true);
16634         this.textNodes = this.el.query('.fc-day-number');
16635         this.cells.addClassOnOver('fc-state-hover');
16636         
16637         var cells = this.cells.elements;
16638         var textEls = this.textNodes;
16639         
16640         Roo.each(cells, function(cell){
16641             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16642         });
16643         
16644         days += startingPos;
16645
16646         // convert everything to numbers so it's fast
16647         var day = 86400000;
16648         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16649         //Roo.log(d);
16650         //Roo.log(pm);
16651         //Roo.log(prevStart);
16652         
16653         var today = new Date().clearTime().getTime();
16654         var sel = date.clearTime().getTime();
16655         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16656         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16657         var ddMatch = this.disabledDatesRE;
16658         var ddText = this.disabledDatesText;
16659         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16660         var ddaysText = this.disabledDaysText;
16661         var format = this.format;
16662         
16663         var setCellClass = function(cal, cell){
16664             cell.row = 0;
16665             cell.events = [];
16666             cell.more = [];
16667             //Roo.log('set Cell Class');
16668             cell.title = "";
16669             var t = d.getTime();
16670             
16671             //Roo.log(d);
16672             
16673             cell.dateValue = t;
16674             if(t == today){
16675                 cell.className += " fc-today";
16676                 cell.className += " fc-state-highlight";
16677                 cell.title = cal.todayText;
16678             }
16679             if(t == sel){
16680                 // disable highlight in other month..
16681                 //cell.className += " fc-state-highlight";
16682                 
16683             }
16684             // disabling
16685             if(t < min) {
16686                 cell.className = " fc-state-disabled";
16687                 cell.title = cal.minText;
16688                 return;
16689             }
16690             if(t > max) {
16691                 cell.className = " fc-state-disabled";
16692                 cell.title = cal.maxText;
16693                 return;
16694             }
16695             if(ddays){
16696                 if(ddays.indexOf(d.getDay()) != -1){
16697                     cell.title = ddaysText;
16698                     cell.className = " fc-state-disabled";
16699                 }
16700             }
16701             if(ddMatch && format){
16702                 var fvalue = d.dateFormat(format);
16703                 if(ddMatch.test(fvalue)){
16704                     cell.title = ddText.replace("%0", fvalue);
16705                     cell.className = " fc-state-disabled";
16706                 }
16707             }
16708             
16709             if (!cell.initialClassName) {
16710                 cell.initialClassName = cell.dom.className;
16711             }
16712             
16713             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16714         };
16715
16716         var i = 0;
16717         
16718         for(; i < startingPos; i++) {
16719             textEls[i].innerHTML = (++prevStart);
16720             d.setDate(d.getDate()+1);
16721             
16722             cells[i].className = "fc-past fc-other-month";
16723             setCellClass(this, cells[i]);
16724         }
16725         
16726         var intDay = 0;
16727         
16728         for(; i < days; i++){
16729             intDay = i - startingPos + 1;
16730             textEls[i].innerHTML = (intDay);
16731             d.setDate(d.getDate()+1);
16732             
16733             cells[i].className = ''; // "x-date-active";
16734             setCellClass(this, cells[i]);
16735         }
16736         var extraDays = 0;
16737         
16738         for(; i < 42; i++) {
16739             textEls[i].innerHTML = (++extraDays);
16740             d.setDate(d.getDate()+1);
16741             
16742             cells[i].className = "fc-future fc-other-month";
16743             setCellClass(this, cells[i]);
16744         }
16745         
16746         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16747         
16748         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16749         
16750         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16751         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16752         
16753         if(totalRows != 6){
16754             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16755             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16756         }
16757         
16758         this.fireEvent('monthchange', this, date);
16759         
16760         
16761         /*
16762         if(!this.internalRender){
16763             var main = this.el.dom.firstChild;
16764             var w = main.offsetWidth;
16765             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16766             Roo.fly(main).setWidth(w);
16767             this.internalRender = true;
16768             // opera does not respect the auto grow header center column
16769             // then, after it gets a width opera refuses to recalculate
16770             // without a second pass
16771             if(Roo.isOpera && !this.secondPass){
16772                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16773                 this.secondPass = true;
16774                 this.update.defer(10, this, [date]);
16775             }
16776         }
16777         */
16778         
16779     },
16780     
16781     findCell : function(dt) {
16782         dt = dt.clearTime().getTime();
16783         var ret = false;
16784         this.cells.each(function(c){
16785             //Roo.log("check " +c.dateValue + '?=' + dt);
16786             if(c.dateValue == dt){
16787                 ret = c;
16788                 return false;
16789             }
16790             return true;
16791         });
16792         
16793         return ret;
16794     },
16795     
16796     findCells : function(ev) {
16797         var s = ev.start.clone().clearTime().getTime();
16798        // Roo.log(s);
16799         var e= ev.end.clone().clearTime().getTime();
16800        // Roo.log(e);
16801         var ret = [];
16802         this.cells.each(function(c){
16803              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16804             
16805             if(c.dateValue > e){
16806                 return ;
16807             }
16808             if(c.dateValue < s){
16809                 return ;
16810             }
16811             ret.push(c);
16812         });
16813         
16814         return ret;    
16815     },
16816     
16817 //    findBestRow: function(cells)
16818 //    {
16819 //        var ret = 0;
16820 //        
16821 //        for (var i =0 ; i < cells.length;i++) {
16822 //            ret  = Math.max(cells[i].rows || 0,ret);
16823 //        }
16824 //        return ret;
16825 //        
16826 //    },
16827     
16828     
16829     addItem : function(ev)
16830     {
16831         // look for vertical location slot in
16832         var cells = this.findCells(ev);
16833         
16834 //        ev.row = this.findBestRow(cells);
16835         
16836         // work out the location.
16837         
16838         var crow = false;
16839         var rows = [];
16840         for(var i =0; i < cells.length; i++) {
16841             
16842             cells[i].row = cells[0].row;
16843             
16844             if(i == 0){
16845                 cells[i].row = cells[i].row + 1;
16846             }
16847             
16848             if (!crow) {
16849                 crow = {
16850                     start : cells[i],
16851                     end :  cells[i]
16852                 };
16853                 continue;
16854             }
16855             if (crow.start.getY() == cells[i].getY()) {
16856                 // on same row.
16857                 crow.end = cells[i];
16858                 continue;
16859             }
16860             // different row.
16861             rows.push(crow);
16862             crow = {
16863                 start: cells[i],
16864                 end : cells[i]
16865             };
16866             
16867         }
16868         
16869         rows.push(crow);
16870         ev.els = [];
16871         ev.rows = rows;
16872         ev.cells = cells;
16873         
16874         cells[0].events.push(ev);
16875         
16876         this.calevents.push(ev);
16877     },
16878     
16879     clearEvents: function() {
16880         
16881         if(!this.calevents){
16882             return;
16883         }
16884         
16885         Roo.each(this.cells.elements, function(c){
16886             c.row = 0;
16887             c.events = [];
16888             c.more = [];
16889         });
16890         
16891         Roo.each(this.calevents, function(e) {
16892             Roo.each(e.els, function(el) {
16893                 el.un('mouseenter' ,this.onEventEnter, this);
16894                 el.un('mouseleave' ,this.onEventLeave, this);
16895                 el.remove();
16896             },this);
16897         },this);
16898         
16899         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16900             e.remove();
16901         });
16902         
16903     },
16904     
16905     renderEvents: function()
16906     {   
16907         var _this = this;
16908         
16909         this.cells.each(function(c) {
16910             
16911             if(c.row < 5){
16912                 return;
16913             }
16914             
16915             var ev = c.events;
16916             
16917             var r = 4;
16918             if(c.row != c.events.length){
16919                 r = 4 - (4 - (c.row - c.events.length));
16920             }
16921             
16922             c.events = ev.slice(0, r);
16923             c.more = ev.slice(r);
16924             
16925             if(c.more.length && c.more.length == 1){
16926                 c.events.push(c.more.pop());
16927             }
16928             
16929             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16930             
16931         });
16932             
16933         this.cells.each(function(c) {
16934             
16935             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16936             
16937             
16938             for (var e = 0; e < c.events.length; e++){
16939                 var ev = c.events[e];
16940                 var rows = ev.rows;
16941                 
16942                 for(var i = 0; i < rows.length; i++) {
16943                 
16944                     // how many rows should it span..
16945
16946                     var  cfg = {
16947                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16948                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16949
16950                         unselectable : "on",
16951                         cn : [
16952                             {
16953                                 cls: 'fc-event-inner',
16954                                 cn : [
16955     //                                {
16956     //                                  tag:'span',
16957     //                                  cls: 'fc-event-time',
16958     //                                  html : cells.length > 1 ? '' : ev.time
16959     //                                },
16960                                     {
16961                                       tag:'span',
16962                                       cls: 'fc-event-title',
16963                                       html : String.format('{0}', ev.title)
16964                                     }
16965
16966
16967                                 ]
16968                             },
16969                             {
16970                                 cls: 'ui-resizable-handle ui-resizable-e',
16971                                 html : '&nbsp;&nbsp;&nbsp'
16972                             }
16973
16974                         ]
16975                     };
16976
16977                     if (i == 0) {
16978                         cfg.cls += ' fc-event-start';
16979                     }
16980                     if ((i+1) == rows.length) {
16981                         cfg.cls += ' fc-event-end';
16982                     }
16983
16984                     var ctr = _this.el.select('.fc-event-container',true).first();
16985                     var cg = ctr.createChild(cfg);
16986
16987                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16988                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16989
16990                     var r = (c.more.length) ? 1 : 0;
16991                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16992                     cg.setWidth(ebox.right - sbox.x -2);
16993
16994                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16995                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16996                     cg.on('click', _this.onEventClick, _this, ev);
16997
16998                     ev.els.push(cg);
16999                     
17000                 }
17001                 
17002             }
17003             
17004             
17005             if(c.more.length){
17006                 var  cfg = {
17007                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17008                     style : 'position: absolute',
17009                     unselectable : "on",
17010                     cn : [
17011                         {
17012                             cls: 'fc-event-inner',
17013                             cn : [
17014                                 {
17015                                   tag:'span',
17016                                   cls: 'fc-event-title',
17017                                   html : 'More'
17018                                 }
17019
17020
17021                             ]
17022                         },
17023                         {
17024                             cls: 'ui-resizable-handle ui-resizable-e',
17025                             html : '&nbsp;&nbsp;&nbsp'
17026                         }
17027
17028                     ]
17029                 };
17030
17031                 var ctr = _this.el.select('.fc-event-container',true).first();
17032                 var cg = ctr.createChild(cfg);
17033
17034                 var sbox = c.select('.fc-day-content',true).first().getBox();
17035                 var ebox = c.select('.fc-day-content',true).first().getBox();
17036                 //Roo.log(cg);
17037                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17038                 cg.setWidth(ebox.right - sbox.x -2);
17039
17040                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17041                 
17042             }
17043             
17044         });
17045         
17046         
17047         
17048     },
17049     
17050     onEventEnter: function (e, el,event,d) {
17051         this.fireEvent('evententer', this, el, event);
17052     },
17053     
17054     onEventLeave: function (e, el,event,d) {
17055         this.fireEvent('eventleave', this, el, event);
17056     },
17057     
17058     onEventClick: function (e, el,event,d) {
17059         this.fireEvent('eventclick', this, el, event);
17060     },
17061     
17062     onMonthChange: function () {
17063         this.store.load();
17064     },
17065     
17066     onMoreEventClick: function(e, el, more)
17067     {
17068         var _this = this;
17069         
17070         this.calpopover.placement = 'right';
17071         this.calpopover.setTitle('More');
17072         
17073         this.calpopover.setContent('');
17074         
17075         var ctr = this.calpopover.el.select('.popover-content', true).first();
17076         
17077         Roo.each(more, function(m){
17078             var cfg = {
17079                 cls : 'fc-event-hori fc-event-draggable',
17080                 html : m.title
17081             };
17082             var cg = ctr.createChild(cfg);
17083             
17084             cg.on('click', _this.onEventClick, _this, m);
17085         });
17086         
17087         this.calpopover.show(el);
17088         
17089         
17090     },
17091     
17092     onLoad: function () 
17093     {   
17094         this.calevents = [];
17095         var cal = this;
17096         
17097         if(this.store.getCount() > 0){
17098             this.store.data.each(function(d){
17099                cal.addItem({
17100                     id : d.data.id,
17101                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17102                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17103                     time : d.data.start_time,
17104                     title : d.data.title,
17105                     description : d.data.description,
17106                     venue : d.data.venue
17107                 });
17108             });
17109         }
17110         
17111         this.renderEvents();
17112         
17113         if(this.calevents.length && this.loadMask){
17114             this.maskEl.hide();
17115         }
17116     },
17117     
17118     onBeforeLoad: function()
17119     {
17120         this.clearEvents();
17121         if(this.loadMask){
17122             this.maskEl.show();
17123         }
17124     }
17125 });
17126
17127  
17128  /*
17129  * - LGPL
17130  *
17131  * element
17132  * 
17133  */
17134
17135 /**
17136  * @class Roo.bootstrap.Popover
17137  * @extends Roo.bootstrap.Component
17138  * Bootstrap Popover class
17139  * @cfg {String} html contents of the popover   (or false to use children..)
17140  * @cfg {String} title of popover (or false to hide)
17141  * @cfg {String} placement how it is placed
17142  * @cfg {String} trigger click || hover (or false to trigger manually)
17143  * @cfg {String} over what (parent or false to trigger manually.)
17144  * @cfg {Number} delay - delay before showing
17145  
17146  * @constructor
17147  * Create a new Popover
17148  * @param {Object} config The config object
17149  */
17150
17151 Roo.bootstrap.Popover = function(config){
17152     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17153     
17154     this.addEvents({
17155         // raw events
17156          /**
17157          * @event show
17158          * After the popover show
17159          * 
17160          * @param {Roo.bootstrap.Popover} this
17161          */
17162         "show" : true,
17163         /**
17164          * @event hide
17165          * After the popover hide
17166          * 
17167          * @param {Roo.bootstrap.Popover} this
17168          */
17169         "hide" : true
17170     });
17171 };
17172
17173 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17174     
17175     title: 'Fill in a title',
17176     html: false,
17177     
17178     placement : 'right',
17179     trigger : 'hover', // hover
17180     
17181     delay : 0,
17182     
17183     over: 'parent',
17184     
17185     can_build_overlaid : false,
17186     
17187     getChildContainer : function()
17188     {
17189         return this.el.select('.popover-content',true).first();
17190     },
17191     
17192     getAutoCreate : function(){
17193          
17194         var cfg = {
17195            cls : 'popover roo-dynamic',
17196            style: 'display:block',
17197            cn : [
17198                 {
17199                     cls : 'arrow'
17200                 },
17201                 {
17202                     cls : 'popover-inner',
17203                     cn : [
17204                         {
17205                             tag: 'h3',
17206                             cls: 'popover-title',
17207                             html : this.title
17208                         },
17209                         {
17210                             cls : 'popover-content',
17211                             html : this.html
17212                         }
17213                     ]
17214                     
17215                 }
17216            ]
17217         };
17218         
17219         return cfg;
17220     },
17221     setTitle: function(str)
17222     {
17223         this.title = str;
17224         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17225     },
17226     setContent: function(str)
17227     {
17228         this.html = str;
17229         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17230     },
17231     // as it get's added to the bottom of the page.
17232     onRender : function(ct, position)
17233     {
17234         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17235         if(!this.el){
17236             var cfg = Roo.apply({},  this.getAutoCreate());
17237             cfg.id = Roo.id();
17238             
17239             if (this.cls) {
17240                 cfg.cls += ' ' + this.cls;
17241             }
17242             if (this.style) {
17243                 cfg.style = this.style;
17244             }
17245             //Roo.log("adding to ");
17246             this.el = Roo.get(document.body).createChild(cfg, position);
17247 //            Roo.log(this.el);
17248         }
17249         this.initEvents();
17250     },
17251     
17252     initEvents : function()
17253     {
17254         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17255         this.el.enableDisplayMode('block');
17256         this.el.hide();
17257         if (this.over === false) {
17258             return; 
17259         }
17260         if (this.triggers === false) {
17261             return;
17262         }
17263         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17264         var triggers = this.trigger ? this.trigger.split(' ') : [];
17265         Roo.each(triggers, function(trigger) {
17266         
17267             if (trigger == 'click') {
17268                 on_el.on('click', this.toggle, this);
17269             } else if (trigger != 'manual') {
17270                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17271                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17272       
17273                 on_el.on(eventIn  ,this.enter, this);
17274                 on_el.on(eventOut, this.leave, this);
17275             }
17276         }, this);
17277         
17278     },
17279     
17280     
17281     // private
17282     timeout : null,
17283     hoverState : null,
17284     
17285     toggle : function () {
17286         this.hoverState == 'in' ? this.leave() : this.enter();
17287     },
17288     
17289     enter : function () {
17290         
17291         clearTimeout(this.timeout);
17292     
17293         this.hoverState = 'in';
17294     
17295         if (!this.delay || !this.delay.show) {
17296             this.show();
17297             return;
17298         }
17299         var _t = this;
17300         this.timeout = setTimeout(function () {
17301             if (_t.hoverState == 'in') {
17302                 _t.show();
17303             }
17304         }, this.delay.show)
17305     },
17306     
17307     leave : function() {
17308         clearTimeout(this.timeout);
17309     
17310         this.hoverState = 'out';
17311     
17312         if (!this.delay || !this.delay.hide) {
17313             this.hide();
17314             return;
17315         }
17316         var _t = this;
17317         this.timeout = setTimeout(function () {
17318             if (_t.hoverState == 'out') {
17319                 _t.hide();
17320             }
17321         }, this.delay.hide)
17322     },
17323     
17324     show : function (on_el)
17325     {
17326         if (!on_el) {
17327             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17328         }
17329         
17330         // set content.
17331         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17332         if (this.html !== false) {
17333             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17334         }
17335         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17336         if (!this.title.length) {
17337             this.el.select('.popover-title',true).hide();
17338         }
17339         
17340         var placement = typeof this.placement == 'function' ?
17341             this.placement.call(this, this.el, on_el) :
17342             this.placement;
17343             
17344         var autoToken = /\s?auto?\s?/i;
17345         var autoPlace = autoToken.test(placement);
17346         if (autoPlace) {
17347             placement = placement.replace(autoToken, '') || 'top';
17348         }
17349         
17350         //this.el.detach()
17351         //this.el.setXY([0,0]);
17352         this.el.show();
17353         this.el.dom.style.display='block';
17354         this.el.addClass(placement);
17355         
17356         //this.el.appendTo(on_el);
17357         
17358         var p = this.getPosition();
17359         var box = this.el.getBox();
17360         
17361         if (autoPlace) {
17362             // fixme..
17363         }
17364         var align = Roo.bootstrap.Popover.alignment[placement];
17365         
17366 //        Roo.log(align);
17367         this.el.alignTo(on_el, align[0],align[1]);
17368         //var arrow = this.el.select('.arrow',true).first();
17369         //arrow.set(align[2], 
17370         
17371         this.el.addClass('in');
17372         
17373         
17374         if (this.el.hasClass('fade')) {
17375             // fade it?
17376         }
17377         
17378         this.hoverState = 'in';
17379         
17380         this.fireEvent('show', this);
17381         
17382     },
17383     hide : function()
17384     {
17385         this.el.setXY([0,0]);
17386         this.el.removeClass('in');
17387         this.el.hide();
17388         this.hoverState = null;
17389         
17390         this.fireEvent('hide', this);
17391     }
17392     
17393 });
17394
17395 Roo.bootstrap.Popover.alignment = {
17396     'left' : ['r-l', [-10,0], 'right'],
17397     'right' : ['l-r', [10,0], 'left'],
17398     'bottom' : ['t-b', [0,10], 'top'],
17399     'top' : [ 'b-t', [0,-10], 'bottom']
17400 };
17401
17402  /*
17403  * - LGPL
17404  *
17405  * Progress
17406  * 
17407  */
17408
17409 /**
17410  * @class Roo.bootstrap.Progress
17411  * @extends Roo.bootstrap.Component
17412  * Bootstrap Progress class
17413  * @cfg {Boolean} striped striped of the progress bar
17414  * @cfg {Boolean} active animated of the progress bar
17415  * 
17416  * 
17417  * @constructor
17418  * Create a new Progress
17419  * @param {Object} config The config object
17420  */
17421
17422 Roo.bootstrap.Progress = function(config){
17423     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17424 };
17425
17426 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17427     
17428     striped : false,
17429     active: false,
17430     
17431     getAutoCreate : function(){
17432         var cfg = {
17433             tag: 'div',
17434             cls: 'progress'
17435         };
17436         
17437         
17438         if(this.striped){
17439             cfg.cls += ' progress-striped';
17440         }
17441       
17442         if(this.active){
17443             cfg.cls += ' active';
17444         }
17445         
17446         
17447         return cfg;
17448     }
17449    
17450 });
17451
17452  
17453
17454  /*
17455  * - LGPL
17456  *
17457  * ProgressBar
17458  * 
17459  */
17460
17461 /**
17462  * @class Roo.bootstrap.ProgressBar
17463  * @extends Roo.bootstrap.Component
17464  * Bootstrap ProgressBar class
17465  * @cfg {Number} aria_valuenow aria-value now
17466  * @cfg {Number} aria_valuemin aria-value min
17467  * @cfg {Number} aria_valuemax aria-value max
17468  * @cfg {String} label label for the progress bar
17469  * @cfg {String} panel (success | info | warning | danger )
17470  * @cfg {String} role role of the progress bar
17471  * @cfg {String} sr_only text
17472  * 
17473  * 
17474  * @constructor
17475  * Create a new ProgressBar
17476  * @param {Object} config The config object
17477  */
17478
17479 Roo.bootstrap.ProgressBar = function(config){
17480     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17481 };
17482
17483 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17484     
17485     aria_valuenow : 0,
17486     aria_valuemin : 0,
17487     aria_valuemax : 100,
17488     label : false,
17489     panel : false,
17490     role : false,
17491     sr_only: false,
17492     
17493     getAutoCreate : function()
17494     {
17495         
17496         var cfg = {
17497             tag: 'div',
17498             cls: 'progress-bar',
17499             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17500         };
17501         
17502         if(this.sr_only){
17503             cfg.cn = {
17504                 tag: 'span',
17505                 cls: 'sr-only',
17506                 html: this.sr_only
17507             }
17508         }
17509         
17510         if(this.role){
17511             cfg.role = this.role;
17512         }
17513         
17514         if(this.aria_valuenow){
17515             cfg['aria-valuenow'] = this.aria_valuenow;
17516         }
17517         
17518         if(this.aria_valuemin){
17519             cfg['aria-valuemin'] = this.aria_valuemin;
17520         }
17521         
17522         if(this.aria_valuemax){
17523             cfg['aria-valuemax'] = this.aria_valuemax;
17524         }
17525         
17526         if(this.label && !this.sr_only){
17527             cfg.html = this.label;
17528         }
17529         
17530         if(this.panel){
17531             cfg.cls += ' progress-bar-' + this.panel;
17532         }
17533         
17534         return cfg;
17535     },
17536     
17537     update : function(aria_valuenow)
17538     {
17539         this.aria_valuenow = aria_valuenow;
17540         
17541         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17542     }
17543    
17544 });
17545
17546  
17547
17548  /*
17549  * - LGPL
17550  *
17551  * column
17552  * 
17553  */
17554
17555 /**
17556  * @class Roo.bootstrap.TabGroup
17557  * @extends Roo.bootstrap.Column
17558  * Bootstrap Column class
17559  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17560  * @cfg {Boolean} carousel true to make the group behave like a carousel
17561  * @cfg {Boolean} bullets show bullets for the panels
17562  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17563  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17564  * @cfg {Boolean} showarrow (true|false) show arrow default true
17565  * 
17566  * @constructor
17567  * Create a new TabGroup
17568  * @param {Object} config The config object
17569  */
17570
17571 Roo.bootstrap.TabGroup = function(config){
17572     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17573     if (!this.navId) {
17574         this.navId = Roo.id();
17575     }
17576     this.tabs = [];
17577     Roo.bootstrap.TabGroup.register(this);
17578     
17579 };
17580
17581 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17582     
17583     carousel : false,
17584     transition : false,
17585     bullets : 0,
17586     timer : 0,
17587     autoslide : false,
17588     slideFn : false,
17589     slideOnTouch : false,
17590     showarrow : true,
17591     
17592     getAutoCreate : function()
17593     {
17594         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17595         
17596         cfg.cls += ' tab-content';
17597         
17598         if (this.carousel) {
17599             cfg.cls += ' carousel slide';
17600             
17601             cfg.cn = [{
17602                cls : 'carousel-inner',
17603                cn : []
17604             }];
17605         
17606             if(this.bullets  && !Roo.isTouch){
17607                 
17608                 var bullets = {
17609                     cls : 'carousel-bullets',
17610                     cn : []
17611                 };
17612                
17613                 if(this.bullets_cls){
17614                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17615                 }
17616                 
17617                 bullets.cn.push({
17618                     cls : 'clear'
17619                 });
17620                 
17621                 cfg.cn[0].cn.push(bullets);
17622             }
17623             
17624             if(this.showarrow){
17625                 cfg.cn[0].cn.push({
17626                     tag : 'div',
17627                     class : 'carousel-arrow',
17628                     cn : [
17629                         {
17630                             tag : 'div',
17631                             class : 'carousel-prev',
17632                             cn : [
17633                                 {
17634                                     tag : 'i',
17635                                     class : 'fa fa-chevron-left'
17636                                 }
17637                             ]
17638                         },
17639                         {
17640                             tag : 'div',
17641                             class : 'carousel-next',
17642                             cn : [
17643                                 {
17644                                     tag : 'i',
17645                                     class : 'fa fa-chevron-right'
17646                                 }
17647                             ]
17648                         }
17649                     ]
17650                 });
17651             }
17652             
17653         }
17654         
17655         return cfg;
17656     },
17657     
17658     initEvents:  function()
17659     {
17660 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17661 //            this.el.on("touchstart", this.onTouchStart, this);
17662 //        }
17663         
17664         if(this.autoslide){
17665             var _this = this;
17666             
17667             this.slideFn = window.setInterval(function() {
17668                 _this.showPanelNext();
17669             }, this.timer);
17670         }
17671         
17672         if(this.showarrow){
17673             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17674             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17675         }
17676         
17677         
17678     },
17679     
17680 //    onTouchStart : function(e, el, o)
17681 //    {
17682 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17683 //            return;
17684 //        }
17685 //        
17686 //        this.showPanelNext();
17687 //    },
17688     
17689     
17690     getChildContainer : function()
17691     {
17692         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17693     },
17694     
17695     /**
17696     * register a Navigation item
17697     * @param {Roo.bootstrap.NavItem} the navitem to add
17698     */
17699     register : function(item)
17700     {
17701         this.tabs.push( item);
17702         item.navId = this.navId; // not really needed..
17703         this.addBullet();
17704     
17705     },
17706     
17707     getActivePanel : function()
17708     {
17709         var r = false;
17710         Roo.each(this.tabs, function(t) {
17711             if (t.active) {
17712                 r = t;
17713                 return false;
17714             }
17715             return null;
17716         });
17717         return r;
17718         
17719     },
17720     getPanelByName : function(n)
17721     {
17722         var r = false;
17723         Roo.each(this.tabs, function(t) {
17724             if (t.tabId == n) {
17725                 r = t;
17726                 return false;
17727             }
17728             return null;
17729         });
17730         return r;
17731     },
17732     indexOfPanel : function(p)
17733     {
17734         var r = false;
17735         Roo.each(this.tabs, function(t,i) {
17736             if (t.tabId == p.tabId) {
17737                 r = i;
17738                 return false;
17739             }
17740             return null;
17741         });
17742         return r;
17743     },
17744     /**
17745      * show a specific panel
17746      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17747      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17748      */
17749     showPanel : function (pan)
17750     {
17751         if(this.transition || typeof(pan) == 'undefined'){
17752             Roo.log("waiting for the transitionend");
17753             return;
17754         }
17755         
17756         if (typeof(pan) == 'number') {
17757             pan = this.tabs[pan];
17758         }
17759         
17760         if (typeof(pan) == 'string') {
17761             pan = this.getPanelByName(pan);
17762         }
17763         
17764         var cur = this.getActivePanel();
17765         
17766         if(!pan || !cur){
17767             Roo.log('pan or acitve pan is undefined');
17768             return false;
17769         }
17770         
17771         if (pan.tabId == this.getActivePanel().tabId) {
17772             return true;
17773         }
17774         
17775         if (false === cur.fireEvent('beforedeactivate')) {
17776             return false;
17777         }
17778         
17779         if(this.bullets > 0 && !Roo.isTouch){
17780             this.setActiveBullet(this.indexOfPanel(pan));
17781         }
17782         
17783         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17784             
17785             this.transition = true;
17786             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17787             var lr = dir == 'next' ? 'left' : 'right';
17788             pan.el.addClass(dir); // or prev
17789             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17790             cur.el.addClass(lr); // or right
17791             pan.el.addClass(lr);
17792             
17793             var _this = this;
17794             cur.el.on('transitionend', function() {
17795                 Roo.log("trans end?");
17796                 
17797                 pan.el.removeClass([lr,dir]);
17798                 pan.setActive(true);
17799                 
17800                 cur.el.removeClass([lr]);
17801                 cur.setActive(false);
17802                 
17803                 _this.transition = false;
17804                 
17805             }, this, { single:  true } );
17806             
17807             return true;
17808         }
17809         
17810         cur.setActive(false);
17811         pan.setActive(true);
17812         
17813         return true;
17814         
17815     },
17816     showPanelNext : function()
17817     {
17818         var i = this.indexOfPanel(this.getActivePanel());
17819         
17820         if (i >= this.tabs.length - 1 && !this.autoslide) {
17821             return;
17822         }
17823         
17824         if (i >= this.tabs.length - 1 && this.autoslide) {
17825             i = -1;
17826         }
17827         
17828         this.showPanel(this.tabs[i+1]);
17829     },
17830     
17831     showPanelPrev : function()
17832     {
17833         var i = this.indexOfPanel(this.getActivePanel());
17834         
17835         if (i  < 1 && !this.autoslide) {
17836             return;
17837         }
17838         
17839         if (i < 1 && this.autoslide) {
17840             i = this.tabs.length;
17841         }
17842         
17843         this.showPanel(this.tabs[i-1]);
17844     },
17845     
17846     
17847     addBullet: function()
17848     {
17849         if(!this.bullets || Roo.isTouch){
17850             return;
17851         }
17852         var ctr = this.el.select('.carousel-bullets',true).first();
17853         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17854         var bullet = ctr.createChild({
17855             cls : 'bullet bullet-' + i
17856         },ctr.dom.lastChild);
17857         
17858         
17859         var _this = this;
17860         
17861         bullet.on('click', (function(e, el, o, ii, t){
17862
17863             e.preventDefault();
17864
17865             this.showPanel(ii);
17866
17867             if(this.autoslide && this.slideFn){
17868                 clearInterval(this.slideFn);
17869                 this.slideFn = window.setInterval(function() {
17870                     _this.showPanelNext();
17871                 }, this.timer);
17872             }
17873
17874         }).createDelegate(this, [i, bullet], true));
17875                 
17876         
17877     },
17878      
17879     setActiveBullet : function(i)
17880     {
17881         if(Roo.isTouch){
17882             return;
17883         }
17884         
17885         Roo.each(this.el.select('.bullet', true).elements, function(el){
17886             el.removeClass('selected');
17887         });
17888
17889         var bullet = this.el.select('.bullet-' + i, true).first();
17890         
17891         if(!bullet){
17892             return;
17893         }
17894         
17895         bullet.addClass('selected');
17896     }
17897     
17898     
17899   
17900 });
17901
17902  
17903
17904  
17905  
17906 Roo.apply(Roo.bootstrap.TabGroup, {
17907     
17908     groups: {},
17909      /**
17910     * register a Navigation Group
17911     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17912     */
17913     register : function(navgrp)
17914     {
17915         this.groups[navgrp.navId] = navgrp;
17916         
17917     },
17918     /**
17919     * fetch a Navigation Group based on the navigation ID
17920     * if one does not exist , it will get created.
17921     * @param {string} the navgroup to add
17922     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17923     */
17924     get: function(navId) {
17925         if (typeof(this.groups[navId]) == 'undefined') {
17926             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17927         }
17928         return this.groups[navId] ;
17929     }
17930     
17931     
17932     
17933 });
17934
17935  /*
17936  * - LGPL
17937  *
17938  * TabPanel
17939  * 
17940  */
17941
17942 /**
17943  * @class Roo.bootstrap.TabPanel
17944  * @extends Roo.bootstrap.Component
17945  * Bootstrap TabPanel class
17946  * @cfg {Boolean} active panel active
17947  * @cfg {String} html panel content
17948  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17949  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17950  * @cfg {String} href click to link..
17951  * 
17952  * 
17953  * @constructor
17954  * Create a new TabPanel
17955  * @param {Object} config The config object
17956  */
17957
17958 Roo.bootstrap.TabPanel = function(config){
17959     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17960     this.addEvents({
17961         /**
17962              * @event changed
17963              * Fires when the active status changes
17964              * @param {Roo.bootstrap.TabPanel} this
17965              * @param {Boolean} state the new state
17966             
17967          */
17968         'changed': true,
17969         /**
17970              * @event beforedeactivate
17971              * Fires before a tab is de-activated - can be used to do validation on a form.
17972              * @param {Roo.bootstrap.TabPanel} this
17973              * @return {Boolean} false if there is an error
17974             
17975          */
17976         'beforedeactivate': true
17977      });
17978     
17979     this.tabId = this.tabId || Roo.id();
17980   
17981 };
17982
17983 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17984     
17985     active: false,
17986     html: false,
17987     tabId: false,
17988     navId : false,
17989     href : '',
17990     
17991     getAutoCreate : function(){
17992         var cfg = {
17993             tag: 'div',
17994             // item is needed for carousel - not sure if it has any effect otherwise
17995             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17996             html: this.html || ''
17997         };
17998         
17999         if(this.active){
18000             cfg.cls += ' active';
18001         }
18002         
18003         if(this.tabId){
18004             cfg.tabId = this.tabId;
18005         }
18006         
18007         
18008         return cfg;
18009     },
18010     
18011     initEvents:  function()
18012     {
18013         var p = this.parent();
18014         
18015         this.navId = this.navId || p.navId;
18016         
18017         if (typeof(this.navId) != 'undefined') {
18018             // not really needed.. but just in case.. parent should be a NavGroup.
18019             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18020             
18021             tg.register(this);
18022             
18023             var i = tg.tabs.length - 1;
18024             
18025             if(this.active && tg.bullets > 0 && i < tg.bullets){
18026                 tg.setActiveBullet(i);
18027             }
18028         }
18029         
18030         this.el.on('click', this.onClick, this);
18031         
18032         if(Roo.isTouch){
18033             this.el.on("touchstart", this.onTouchStart, this);
18034             this.el.on("touchmove", this.onTouchMove, this);
18035             this.el.on("touchend", this.onTouchEnd, this);
18036         }
18037         
18038     },
18039     
18040     onRender : function(ct, position)
18041     {
18042         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18043     },
18044     
18045     setActive : function(state)
18046     {
18047         Roo.log("panel - set active " + this.tabId + "=" + state);
18048         
18049         this.active = state;
18050         if (!state) {
18051             this.el.removeClass('active');
18052             
18053         } else  if (!this.el.hasClass('active')) {
18054             this.el.addClass('active');
18055         }
18056         
18057         this.fireEvent('changed', this, state);
18058     },
18059     
18060     onClick : function(e)
18061     {
18062         e.preventDefault();
18063         
18064         if(!this.href.length){
18065             return;
18066         }
18067         
18068         window.location.href = this.href;
18069     },
18070     
18071     startX : 0,
18072     startY : 0,
18073     endX : 0,
18074     endY : 0,
18075     swiping : false,
18076     
18077     onTouchStart : function(e)
18078     {
18079         this.swiping = false;
18080         
18081         this.startX = e.browserEvent.touches[0].clientX;
18082         this.startY = e.browserEvent.touches[0].clientY;
18083     },
18084     
18085     onTouchMove : function(e)
18086     {
18087         this.swiping = true;
18088         
18089         this.endX = e.browserEvent.touches[0].clientX;
18090         this.endY = e.browserEvent.touches[0].clientY;
18091     },
18092     
18093     onTouchEnd : function(e)
18094     {
18095         if(!this.swiping){
18096             this.onClick(e);
18097             return;
18098         }
18099         
18100         var tabGroup = this.parent();
18101         
18102         if(this.endX > this.startX){ // swiping right
18103             tabGroup.showPanelPrev();
18104             return;
18105         }
18106         
18107         if(this.startX > this.endX){ // swiping left
18108             tabGroup.showPanelNext();
18109             return;
18110         }
18111     }
18112     
18113     
18114 });
18115  
18116
18117  
18118
18119  /*
18120  * - LGPL
18121  *
18122  * DateField
18123  * 
18124  */
18125
18126 /**
18127  * @class Roo.bootstrap.DateField
18128  * @extends Roo.bootstrap.Input
18129  * Bootstrap DateField class
18130  * @cfg {Number} weekStart default 0
18131  * @cfg {String} viewMode default empty, (months|years)
18132  * @cfg {String} minViewMode default empty, (months|years)
18133  * @cfg {Number} startDate default -Infinity
18134  * @cfg {Number} endDate default Infinity
18135  * @cfg {Boolean} todayHighlight default false
18136  * @cfg {Boolean} todayBtn default false
18137  * @cfg {Boolean} calendarWeeks default false
18138  * @cfg {Object} daysOfWeekDisabled default empty
18139  * @cfg {Boolean} singleMode default false (true | false)
18140  * 
18141  * @cfg {Boolean} keyboardNavigation default true
18142  * @cfg {String} language default en
18143  * 
18144  * @constructor
18145  * Create a new DateField
18146  * @param {Object} config The config object
18147  */
18148
18149 Roo.bootstrap.DateField = function(config){
18150     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18151      this.addEvents({
18152             /**
18153              * @event show
18154              * Fires when this field show.
18155              * @param {Roo.bootstrap.DateField} this
18156              * @param {Mixed} date The date value
18157              */
18158             show : true,
18159             /**
18160              * @event show
18161              * Fires when this field hide.
18162              * @param {Roo.bootstrap.DateField} this
18163              * @param {Mixed} date The date value
18164              */
18165             hide : true,
18166             /**
18167              * @event select
18168              * Fires when select a date.
18169              * @param {Roo.bootstrap.DateField} this
18170              * @param {Mixed} date The date value
18171              */
18172             select : true,
18173             /**
18174              * @event beforeselect
18175              * Fires when before select a date.
18176              * @param {Roo.bootstrap.DateField} this
18177              * @param {Mixed} date The date value
18178              */
18179             beforeselect : true
18180         });
18181 };
18182
18183 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18184     
18185     /**
18186      * @cfg {String} format
18187      * The default date format string which can be overriden for localization support.  The format must be
18188      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18189      */
18190     format : "m/d/y",
18191     /**
18192      * @cfg {String} altFormats
18193      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18194      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18195      */
18196     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18197     
18198     weekStart : 0,
18199     
18200     viewMode : '',
18201     
18202     minViewMode : '',
18203     
18204     todayHighlight : false,
18205     
18206     todayBtn: false,
18207     
18208     language: 'en',
18209     
18210     keyboardNavigation: true,
18211     
18212     calendarWeeks: false,
18213     
18214     startDate: -Infinity,
18215     
18216     endDate: Infinity,
18217     
18218     daysOfWeekDisabled: [],
18219     
18220     _events: [],
18221     
18222     singleMode : false,
18223     
18224     UTCDate: function()
18225     {
18226         return new Date(Date.UTC.apply(Date, arguments));
18227     },
18228     
18229     UTCToday: function()
18230     {
18231         var today = new Date();
18232         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18233     },
18234     
18235     getDate: function() {
18236             var d = this.getUTCDate();
18237             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18238     },
18239     
18240     getUTCDate: function() {
18241             return this.date;
18242     },
18243     
18244     setDate: function(d) {
18245             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18246     },
18247     
18248     setUTCDate: function(d) {
18249             this.date = d;
18250             this.setValue(this.formatDate(this.date));
18251     },
18252         
18253     onRender: function(ct, position)
18254     {
18255         
18256         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18257         
18258         this.language = this.language || 'en';
18259         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18260         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18261         
18262         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18263         this.format = this.format || 'm/d/y';
18264         this.isInline = false;
18265         this.isInput = true;
18266         this.component = this.el.select('.add-on', true).first() || false;
18267         this.component = (this.component && this.component.length === 0) ? false : this.component;
18268         this.hasInput = this.component && this.inputEl().length;
18269         
18270         if (typeof(this.minViewMode === 'string')) {
18271             switch (this.minViewMode) {
18272                 case 'months':
18273                     this.minViewMode = 1;
18274                     break;
18275                 case 'years':
18276                     this.minViewMode = 2;
18277                     break;
18278                 default:
18279                     this.minViewMode = 0;
18280                     break;
18281             }
18282         }
18283         
18284         if (typeof(this.viewMode === 'string')) {
18285             switch (this.viewMode) {
18286                 case 'months':
18287                     this.viewMode = 1;
18288                     break;
18289                 case 'years':
18290                     this.viewMode = 2;
18291                     break;
18292                 default:
18293                     this.viewMode = 0;
18294                     break;
18295             }
18296         }
18297                 
18298         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18299         
18300 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18301         
18302         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18303         
18304         this.picker().on('mousedown', this.onMousedown, this);
18305         this.picker().on('click', this.onClick, this);
18306         
18307         this.picker().addClass('datepicker-dropdown');
18308         
18309         this.startViewMode = this.viewMode;
18310         
18311         if(this.singleMode){
18312             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18313                 v.setVisibilityMode(Roo.Element.DISPLAY);
18314                 v.hide();
18315             });
18316             
18317             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18318                 v.setStyle('width', '189px');
18319             });
18320         }
18321         
18322         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18323             if(!this.calendarWeeks){
18324                 v.remove();
18325                 return;
18326             }
18327             
18328             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18329             v.attr('colspan', function(i, val){
18330                 return parseInt(val) + 1;
18331             });
18332         });
18333                         
18334         
18335         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18336         
18337         this.setStartDate(this.startDate);
18338         this.setEndDate(this.endDate);
18339         
18340         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18341         
18342         this.fillDow();
18343         this.fillMonths();
18344         this.update();
18345         this.showMode();
18346         
18347         if(this.isInline) {
18348             this.show();
18349         }
18350     },
18351     
18352     picker : function()
18353     {
18354         return this.pickerEl;
18355 //        return this.el.select('.datepicker', true).first();
18356     },
18357     
18358     fillDow: function()
18359     {
18360         var dowCnt = this.weekStart;
18361         
18362         var dow = {
18363             tag: 'tr',
18364             cn: [
18365                 
18366             ]
18367         };
18368         
18369         if(this.calendarWeeks){
18370             dow.cn.push({
18371                 tag: 'th',
18372                 cls: 'cw',
18373                 html: '&nbsp;'
18374             })
18375         }
18376         
18377         while (dowCnt < this.weekStart + 7) {
18378             dow.cn.push({
18379                 tag: 'th',
18380                 cls: 'dow',
18381                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18382             });
18383         }
18384         
18385         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18386     },
18387     
18388     fillMonths: function()
18389     {    
18390         var i = 0;
18391         var months = this.picker().select('>.datepicker-months td', true).first();
18392         
18393         months.dom.innerHTML = '';
18394         
18395         while (i < 12) {
18396             var month = {
18397                 tag: 'span',
18398                 cls: 'month',
18399                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18400             };
18401             
18402             months.createChild(month);
18403         }
18404         
18405     },
18406     
18407     update: function()
18408     {
18409         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;
18410         
18411         if (this.date < this.startDate) {
18412             this.viewDate = new Date(this.startDate);
18413         } else if (this.date > this.endDate) {
18414             this.viewDate = new Date(this.endDate);
18415         } else {
18416             this.viewDate = new Date(this.date);
18417         }
18418         
18419         this.fill();
18420     },
18421     
18422     fill: function() 
18423     {
18424         var d = new Date(this.viewDate),
18425                 year = d.getUTCFullYear(),
18426                 month = d.getUTCMonth(),
18427                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18428                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18429                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18430                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18431                 currentDate = this.date && this.date.valueOf(),
18432                 today = this.UTCToday();
18433         
18434         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18435         
18436 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18437         
18438 //        this.picker.select('>tfoot th.today').
18439 //                                              .text(dates[this.language].today)
18440 //                                              .toggle(this.todayBtn !== false);
18441     
18442         this.updateNavArrows();
18443         this.fillMonths();
18444                                                 
18445         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18446         
18447         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18448          
18449         prevMonth.setUTCDate(day);
18450         
18451         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18452         
18453         var nextMonth = new Date(prevMonth);
18454         
18455         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18456         
18457         nextMonth = nextMonth.valueOf();
18458         
18459         var fillMonths = false;
18460         
18461         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18462         
18463         while(prevMonth.valueOf() < nextMonth) {
18464             var clsName = '';
18465             
18466             if (prevMonth.getUTCDay() === this.weekStart) {
18467                 if(fillMonths){
18468                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18469                 }
18470                     
18471                 fillMonths = {
18472                     tag: 'tr',
18473                     cn: []
18474                 };
18475                 
18476                 if(this.calendarWeeks){
18477                     // ISO 8601: First week contains first thursday.
18478                     // ISO also states week starts on Monday, but we can be more abstract here.
18479                     var
18480                     // Start of current week: based on weekstart/current date
18481                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18482                     // Thursday of this week
18483                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18484                     // First Thursday of year, year from thursday
18485                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18486                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18487                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18488                     
18489                     fillMonths.cn.push({
18490                         tag: 'td',
18491                         cls: 'cw',
18492                         html: calWeek
18493                     });
18494                 }
18495             }
18496             
18497             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18498                 clsName += ' old';
18499             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18500                 clsName += ' new';
18501             }
18502             if (this.todayHighlight &&
18503                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18504                 prevMonth.getUTCMonth() == today.getMonth() &&
18505                 prevMonth.getUTCDate() == today.getDate()) {
18506                 clsName += ' today';
18507             }
18508             
18509             if (currentDate && prevMonth.valueOf() === currentDate) {
18510                 clsName += ' active';
18511             }
18512             
18513             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18514                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18515                     clsName += ' disabled';
18516             }
18517             
18518             fillMonths.cn.push({
18519                 tag: 'td',
18520                 cls: 'day ' + clsName,
18521                 html: prevMonth.getDate()
18522             });
18523             
18524             prevMonth.setDate(prevMonth.getDate()+1);
18525         }
18526           
18527         var currentYear = this.date && this.date.getUTCFullYear();
18528         var currentMonth = this.date && this.date.getUTCMonth();
18529         
18530         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18531         
18532         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18533             v.removeClass('active');
18534             
18535             if(currentYear === year && k === currentMonth){
18536                 v.addClass('active');
18537             }
18538             
18539             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18540                 v.addClass('disabled');
18541             }
18542             
18543         });
18544         
18545         
18546         year = parseInt(year/10, 10) * 10;
18547         
18548         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18549         
18550         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18551         
18552         year -= 1;
18553         for (var i = -1; i < 11; i++) {
18554             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18555                 tag: 'span',
18556                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18557                 html: year
18558             });
18559             
18560             year += 1;
18561         }
18562     },
18563     
18564     showMode: function(dir) 
18565     {
18566         if (dir) {
18567             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18568         }
18569         
18570         Roo.each(this.picker().select('>div',true).elements, function(v){
18571             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18572             v.hide();
18573         });
18574         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18575     },
18576     
18577     place: function()
18578     {
18579         if(this.isInline) {
18580             return;
18581         }
18582         
18583         this.picker().removeClass(['bottom', 'top']);
18584         
18585         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18586             /*
18587              * place to the top of element!
18588              *
18589              */
18590             
18591             this.picker().addClass('top');
18592             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18593             
18594             return;
18595         }
18596         
18597         this.picker().addClass('bottom');
18598         
18599         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18600     },
18601     
18602     parseDate : function(value)
18603     {
18604         if(!value || value instanceof Date){
18605             return value;
18606         }
18607         var v = Date.parseDate(value, this.format);
18608         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18609             v = Date.parseDate(value, 'Y-m-d');
18610         }
18611         if(!v && this.altFormats){
18612             if(!this.altFormatsArray){
18613                 this.altFormatsArray = this.altFormats.split("|");
18614             }
18615             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18616                 v = Date.parseDate(value, this.altFormatsArray[i]);
18617             }
18618         }
18619         return v;
18620     },
18621     
18622     formatDate : function(date, fmt)
18623     {   
18624         return (!date || !(date instanceof Date)) ?
18625         date : date.dateFormat(fmt || this.format);
18626     },
18627     
18628     onFocus : function()
18629     {
18630         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18631         this.show();
18632     },
18633     
18634     onBlur : function()
18635     {
18636         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18637         
18638         var d = this.inputEl().getValue();
18639         
18640         this.setValue(d);
18641                 
18642         this.hide();
18643     },
18644     
18645     show : function()
18646     {
18647         this.picker().show();
18648         this.update();
18649         this.place();
18650         
18651         this.fireEvent('show', this, this.date);
18652     },
18653     
18654     hide : function()
18655     {
18656         if(this.isInline) {
18657             return;
18658         }
18659         this.picker().hide();
18660         this.viewMode = this.startViewMode;
18661         this.showMode();
18662         
18663         this.fireEvent('hide', this, this.date);
18664         
18665     },
18666     
18667     onMousedown: function(e)
18668     {
18669         e.stopPropagation();
18670         e.preventDefault();
18671     },
18672     
18673     keyup: function(e)
18674     {
18675         Roo.bootstrap.DateField.superclass.keyup.call(this);
18676         this.update();
18677     },
18678
18679     setValue: function(v)
18680     {
18681         if(this.fireEvent('beforeselect', this, v) !== false){
18682             var d = new Date(this.parseDate(v) ).clearTime();
18683         
18684             if(isNaN(d.getTime())){
18685                 this.date = this.viewDate = '';
18686                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18687                 return;
18688             }
18689
18690             v = this.formatDate(d);
18691
18692             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18693
18694             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18695
18696             this.update();
18697
18698             this.fireEvent('select', this, this.date);
18699         }
18700     },
18701     
18702     getValue: function()
18703     {
18704         return this.formatDate(this.date);
18705     },
18706     
18707     fireKey: function(e)
18708     {
18709         if (!this.picker().isVisible()){
18710             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18711                 this.show();
18712             }
18713             return;
18714         }
18715         
18716         var dateChanged = false,
18717         dir, day, month,
18718         newDate, newViewDate;
18719         
18720         switch(e.keyCode){
18721             case 27: // escape
18722                 this.hide();
18723                 e.preventDefault();
18724                 break;
18725             case 37: // left
18726             case 39: // right
18727                 if (!this.keyboardNavigation) {
18728                     break;
18729                 }
18730                 dir = e.keyCode == 37 ? -1 : 1;
18731                 
18732                 if (e.ctrlKey){
18733                     newDate = this.moveYear(this.date, dir);
18734                     newViewDate = this.moveYear(this.viewDate, dir);
18735                 } else if (e.shiftKey){
18736                     newDate = this.moveMonth(this.date, dir);
18737                     newViewDate = this.moveMonth(this.viewDate, dir);
18738                 } else {
18739                     newDate = new Date(this.date);
18740                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18741                     newViewDate = new Date(this.viewDate);
18742                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18743                 }
18744                 if (this.dateWithinRange(newDate)){
18745                     this.date = newDate;
18746                     this.viewDate = newViewDate;
18747                     this.setValue(this.formatDate(this.date));
18748 //                    this.update();
18749                     e.preventDefault();
18750                     dateChanged = true;
18751                 }
18752                 break;
18753             case 38: // up
18754             case 40: // down
18755                 if (!this.keyboardNavigation) {
18756                     break;
18757                 }
18758                 dir = e.keyCode == 38 ? -1 : 1;
18759                 if (e.ctrlKey){
18760                     newDate = this.moveYear(this.date, dir);
18761                     newViewDate = this.moveYear(this.viewDate, dir);
18762                 } else if (e.shiftKey){
18763                     newDate = this.moveMonth(this.date, dir);
18764                     newViewDate = this.moveMonth(this.viewDate, dir);
18765                 } else {
18766                     newDate = new Date(this.date);
18767                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18768                     newViewDate = new Date(this.viewDate);
18769                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18770                 }
18771                 if (this.dateWithinRange(newDate)){
18772                     this.date = newDate;
18773                     this.viewDate = newViewDate;
18774                     this.setValue(this.formatDate(this.date));
18775 //                    this.update();
18776                     e.preventDefault();
18777                     dateChanged = true;
18778                 }
18779                 break;
18780             case 13: // enter
18781                 this.setValue(this.formatDate(this.date));
18782                 this.hide();
18783                 e.preventDefault();
18784                 break;
18785             case 9: // tab
18786                 this.setValue(this.formatDate(this.date));
18787                 this.hide();
18788                 break;
18789             case 16: // shift
18790             case 17: // ctrl
18791             case 18: // alt
18792                 break;
18793             default :
18794                 this.hide();
18795                 
18796         }
18797     },
18798     
18799     
18800     onClick: function(e) 
18801     {
18802         e.stopPropagation();
18803         e.preventDefault();
18804         
18805         var target = e.getTarget();
18806         
18807         if(target.nodeName.toLowerCase() === 'i'){
18808             target = Roo.get(target).dom.parentNode;
18809         }
18810         
18811         var nodeName = target.nodeName;
18812         var className = target.className;
18813         var html = target.innerHTML;
18814         //Roo.log(nodeName);
18815         
18816         switch(nodeName.toLowerCase()) {
18817             case 'th':
18818                 switch(className) {
18819                     case 'switch':
18820                         this.showMode(1);
18821                         break;
18822                     case 'prev':
18823                     case 'next':
18824                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18825                         switch(this.viewMode){
18826                                 case 0:
18827                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18828                                         break;
18829                                 case 1:
18830                                 case 2:
18831                                         this.viewDate = this.moveYear(this.viewDate, dir);
18832                                         break;
18833                         }
18834                         this.fill();
18835                         break;
18836                     case 'today':
18837                         var date = new Date();
18838                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18839 //                        this.fill()
18840                         this.setValue(this.formatDate(this.date));
18841                         
18842                         this.hide();
18843                         break;
18844                 }
18845                 break;
18846             case 'span':
18847                 if (className.indexOf('disabled') < 0) {
18848                     this.viewDate.setUTCDate(1);
18849                     if (className.indexOf('month') > -1) {
18850                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18851                     } else {
18852                         var year = parseInt(html, 10) || 0;
18853                         this.viewDate.setUTCFullYear(year);
18854                         
18855                     }
18856                     
18857                     if(this.singleMode){
18858                         this.setValue(this.formatDate(this.viewDate));
18859                         this.hide();
18860                         return;
18861                     }
18862                     
18863                     this.showMode(-1);
18864                     this.fill();
18865                 }
18866                 break;
18867                 
18868             case 'td':
18869                 //Roo.log(className);
18870                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18871                     var day = parseInt(html, 10) || 1;
18872                     var year = this.viewDate.getUTCFullYear(),
18873                         month = this.viewDate.getUTCMonth();
18874
18875                     if (className.indexOf('old') > -1) {
18876                         if(month === 0 ){
18877                             month = 11;
18878                             year -= 1;
18879                         }else{
18880                             month -= 1;
18881                         }
18882                     } else if (className.indexOf('new') > -1) {
18883                         if (month == 11) {
18884                             month = 0;
18885                             year += 1;
18886                         } else {
18887                             month += 1;
18888                         }
18889                     }
18890                     //Roo.log([year,month,day]);
18891                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18892                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18893 //                    this.fill();
18894                     //Roo.log(this.formatDate(this.date));
18895                     this.setValue(this.formatDate(this.date));
18896                     this.hide();
18897                 }
18898                 break;
18899         }
18900     },
18901     
18902     setStartDate: function(startDate)
18903     {
18904         this.startDate = startDate || -Infinity;
18905         if (this.startDate !== -Infinity) {
18906             this.startDate = this.parseDate(this.startDate);
18907         }
18908         this.update();
18909         this.updateNavArrows();
18910     },
18911
18912     setEndDate: function(endDate)
18913     {
18914         this.endDate = endDate || Infinity;
18915         if (this.endDate !== Infinity) {
18916             this.endDate = this.parseDate(this.endDate);
18917         }
18918         this.update();
18919         this.updateNavArrows();
18920     },
18921     
18922     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18923     {
18924         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18925         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18926             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18927         }
18928         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18929             return parseInt(d, 10);
18930         });
18931         this.update();
18932         this.updateNavArrows();
18933     },
18934     
18935     updateNavArrows: function() 
18936     {
18937         if(this.singleMode){
18938             return;
18939         }
18940         
18941         var d = new Date(this.viewDate),
18942         year = d.getUTCFullYear(),
18943         month = d.getUTCMonth();
18944         
18945         Roo.each(this.picker().select('.prev', true).elements, function(v){
18946             v.show();
18947             switch (this.viewMode) {
18948                 case 0:
18949
18950                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18951                         v.hide();
18952                     }
18953                     break;
18954                 case 1:
18955                 case 2:
18956                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18957                         v.hide();
18958                     }
18959                     break;
18960             }
18961         });
18962         
18963         Roo.each(this.picker().select('.next', true).elements, function(v){
18964             v.show();
18965             switch (this.viewMode) {
18966                 case 0:
18967
18968                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18969                         v.hide();
18970                     }
18971                     break;
18972                 case 1:
18973                 case 2:
18974                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18975                         v.hide();
18976                     }
18977                     break;
18978             }
18979         })
18980     },
18981     
18982     moveMonth: function(date, dir)
18983     {
18984         if (!dir) {
18985             return date;
18986         }
18987         var new_date = new Date(date.valueOf()),
18988         day = new_date.getUTCDate(),
18989         month = new_date.getUTCMonth(),
18990         mag = Math.abs(dir),
18991         new_month, test;
18992         dir = dir > 0 ? 1 : -1;
18993         if (mag == 1){
18994             test = dir == -1
18995             // If going back one month, make sure month is not current month
18996             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18997             ? function(){
18998                 return new_date.getUTCMonth() == month;
18999             }
19000             // If going forward one month, make sure month is as expected
19001             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19002             : function(){
19003                 return new_date.getUTCMonth() != new_month;
19004             };
19005             new_month = month + dir;
19006             new_date.setUTCMonth(new_month);
19007             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19008             if (new_month < 0 || new_month > 11) {
19009                 new_month = (new_month + 12) % 12;
19010             }
19011         } else {
19012             // For magnitudes >1, move one month at a time...
19013             for (var i=0; i<mag; i++) {
19014                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19015                 new_date = this.moveMonth(new_date, dir);
19016             }
19017             // ...then reset the day, keeping it in the new month
19018             new_month = new_date.getUTCMonth();
19019             new_date.setUTCDate(day);
19020             test = function(){
19021                 return new_month != new_date.getUTCMonth();
19022             };
19023         }
19024         // Common date-resetting loop -- if date is beyond end of month, make it
19025         // end of month
19026         while (test()){
19027             new_date.setUTCDate(--day);
19028             new_date.setUTCMonth(new_month);
19029         }
19030         return new_date;
19031     },
19032
19033     moveYear: function(date, dir)
19034     {
19035         return this.moveMonth(date, dir*12);
19036     },
19037
19038     dateWithinRange: function(date)
19039     {
19040         return date >= this.startDate && date <= this.endDate;
19041     },
19042
19043     
19044     remove: function() 
19045     {
19046         this.picker().remove();
19047     },
19048     
19049     validateValue : function(value)
19050     {
19051         if(value.length < 1)  {
19052             if(this.allowBlank){
19053                 return true;
19054             }
19055             return false;
19056         }
19057         
19058         if(value.length < this.minLength){
19059             return false;
19060         }
19061         if(value.length > this.maxLength){
19062             return false;
19063         }
19064         if(this.vtype){
19065             var vt = Roo.form.VTypes;
19066             if(!vt[this.vtype](value, this)){
19067                 return false;
19068             }
19069         }
19070         if(typeof this.validator == "function"){
19071             var msg = this.validator(value);
19072             if(msg !== true){
19073                 return false;
19074             }
19075         }
19076         
19077         if(this.regex && !this.regex.test(value)){
19078             return false;
19079         }
19080         
19081         if(typeof(this.parseDate(value)) == 'undefined'){
19082             return false;
19083         }
19084         
19085         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19086             return false;
19087         }      
19088         
19089         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19090             return false;
19091         } 
19092         
19093         
19094         return true;
19095     }
19096    
19097 });
19098
19099 Roo.apply(Roo.bootstrap.DateField,  {
19100     
19101     head : {
19102         tag: 'thead',
19103         cn: [
19104         {
19105             tag: 'tr',
19106             cn: [
19107             {
19108                 tag: 'th',
19109                 cls: 'prev',
19110                 html: '<i class="fa fa-arrow-left"/>'
19111             },
19112             {
19113                 tag: 'th',
19114                 cls: 'switch',
19115                 colspan: '5'
19116             },
19117             {
19118                 tag: 'th',
19119                 cls: 'next',
19120                 html: '<i class="fa fa-arrow-right"/>'
19121             }
19122
19123             ]
19124         }
19125         ]
19126     },
19127     
19128     content : {
19129         tag: 'tbody',
19130         cn: [
19131         {
19132             tag: 'tr',
19133             cn: [
19134             {
19135                 tag: 'td',
19136                 colspan: '7'
19137             }
19138             ]
19139         }
19140         ]
19141     },
19142     
19143     footer : {
19144         tag: 'tfoot',
19145         cn: [
19146         {
19147             tag: 'tr',
19148             cn: [
19149             {
19150                 tag: 'th',
19151                 colspan: '7',
19152                 cls: 'today'
19153             }
19154                     
19155             ]
19156         }
19157         ]
19158     },
19159     
19160     dates:{
19161         en: {
19162             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19163             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19164             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19165             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19166             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19167             today: "Today"
19168         }
19169     },
19170     
19171     modes: [
19172     {
19173         clsName: 'days',
19174         navFnc: 'Month',
19175         navStep: 1
19176     },
19177     {
19178         clsName: 'months',
19179         navFnc: 'FullYear',
19180         navStep: 1
19181     },
19182     {
19183         clsName: 'years',
19184         navFnc: 'FullYear',
19185         navStep: 10
19186     }]
19187 });
19188
19189 Roo.apply(Roo.bootstrap.DateField,  {
19190   
19191     template : {
19192         tag: 'div',
19193         cls: 'datepicker dropdown-menu roo-dynamic',
19194         cn: [
19195         {
19196             tag: 'div',
19197             cls: 'datepicker-days',
19198             cn: [
19199             {
19200                 tag: 'table',
19201                 cls: 'table-condensed',
19202                 cn:[
19203                 Roo.bootstrap.DateField.head,
19204                 {
19205                     tag: 'tbody'
19206                 },
19207                 Roo.bootstrap.DateField.footer
19208                 ]
19209             }
19210             ]
19211         },
19212         {
19213             tag: 'div',
19214             cls: 'datepicker-months',
19215             cn: [
19216             {
19217                 tag: 'table',
19218                 cls: 'table-condensed',
19219                 cn:[
19220                 Roo.bootstrap.DateField.head,
19221                 Roo.bootstrap.DateField.content,
19222                 Roo.bootstrap.DateField.footer
19223                 ]
19224             }
19225             ]
19226         },
19227         {
19228             tag: 'div',
19229             cls: 'datepicker-years',
19230             cn: [
19231             {
19232                 tag: 'table',
19233                 cls: 'table-condensed',
19234                 cn:[
19235                 Roo.bootstrap.DateField.head,
19236                 Roo.bootstrap.DateField.content,
19237                 Roo.bootstrap.DateField.footer
19238                 ]
19239             }
19240             ]
19241         }
19242         ]
19243     }
19244 });
19245
19246  
19247
19248  /*
19249  * - LGPL
19250  *
19251  * TimeField
19252  * 
19253  */
19254
19255 /**
19256  * @class Roo.bootstrap.TimeField
19257  * @extends Roo.bootstrap.Input
19258  * Bootstrap DateField class
19259  * 
19260  * 
19261  * @constructor
19262  * Create a new TimeField
19263  * @param {Object} config The config object
19264  */
19265
19266 Roo.bootstrap.TimeField = function(config){
19267     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19268     this.addEvents({
19269             /**
19270              * @event show
19271              * Fires when this field show.
19272              * @param {Roo.bootstrap.DateField} thisthis
19273              * @param {Mixed} date The date value
19274              */
19275             show : true,
19276             /**
19277              * @event show
19278              * Fires when this field hide.
19279              * @param {Roo.bootstrap.DateField} this
19280              * @param {Mixed} date The date value
19281              */
19282             hide : true,
19283             /**
19284              * @event select
19285              * Fires when select a date.
19286              * @param {Roo.bootstrap.DateField} this
19287              * @param {Mixed} date The date value
19288              */
19289             select : true
19290         });
19291 };
19292
19293 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19294     
19295     /**
19296      * @cfg {String} format
19297      * The default time format string which can be overriden for localization support.  The format must be
19298      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19299      */
19300     format : "H:i",
19301        
19302     onRender: function(ct, position)
19303     {
19304         
19305         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19306                 
19307         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19308         
19309         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19310         
19311         this.pop = this.picker().select('>.datepicker-time',true).first();
19312         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19313         
19314         this.picker().on('mousedown', this.onMousedown, this);
19315         this.picker().on('click', this.onClick, this);
19316         
19317         this.picker().addClass('datepicker-dropdown');
19318     
19319         this.fillTime();
19320         this.update();
19321             
19322         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19323         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19324         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19325         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19326         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19327         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19328
19329     },
19330     
19331     fireKey: function(e){
19332         if (!this.picker().isVisible()){
19333             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19334                 this.show();
19335             }
19336             return;
19337         }
19338
19339         e.preventDefault();
19340         
19341         switch(e.keyCode){
19342             case 27: // escape
19343                 this.hide();
19344                 break;
19345             case 37: // left
19346             case 39: // right
19347                 this.onTogglePeriod();
19348                 break;
19349             case 38: // up
19350                 this.onIncrementMinutes();
19351                 break;
19352             case 40: // down
19353                 this.onDecrementMinutes();
19354                 break;
19355             case 13: // enter
19356             case 9: // tab
19357                 this.setTime();
19358                 break;
19359         }
19360     },
19361     
19362     onClick: function(e) {
19363         e.stopPropagation();
19364         e.preventDefault();
19365     },
19366     
19367     picker : function()
19368     {
19369         return this.el.select('.datepicker', true).first();
19370     },
19371     
19372     fillTime: function()
19373     {    
19374         var time = this.pop.select('tbody', true).first();
19375         
19376         time.dom.innerHTML = '';
19377         
19378         time.createChild({
19379             tag: 'tr',
19380             cn: [
19381                 {
19382                     tag: 'td',
19383                     cn: [
19384                         {
19385                             tag: 'a',
19386                             href: '#',
19387                             cls: 'btn',
19388                             cn: [
19389                                 {
19390                                     tag: 'span',
19391                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19392                                 }
19393                             ]
19394                         } 
19395                     ]
19396                 },
19397                 {
19398                     tag: 'td',
19399                     cls: 'separator'
19400                 },
19401                 {
19402                     tag: 'td',
19403                     cn: [
19404                         {
19405                             tag: 'a',
19406                             href: '#',
19407                             cls: 'btn',
19408                             cn: [
19409                                 {
19410                                     tag: 'span',
19411                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19412                                 }
19413                             ]
19414                         }
19415                     ]
19416                 },
19417                 {
19418                     tag: 'td',
19419                     cls: 'separator'
19420                 }
19421             ]
19422         });
19423         
19424         time.createChild({
19425             tag: 'tr',
19426             cn: [
19427                 {
19428                     tag: 'td',
19429                     cn: [
19430                         {
19431                             tag: 'span',
19432                             cls: 'timepicker-hour',
19433                             html: '00'
19434                         }  
19435                     ]
19436                 },
19437                 {
19438                     tag: 'td',
19439                     cls: 'separator',
19440                     html: ':'
19441                 },
19442                 {
19443                     tag: 'td',
19444                     cn: [
19445                         {
19446                             tag: 'span',
19447                             cls: 'timepicker-minute',
19448                             html: '00'
19449                         }  
19450                     ]
19451                 },
19452                 {
19453                     tag: 'td',
19454                     cls: 'separator'
19455                 },
19456                 {
19457                     tag: 'td',
19458                     cn: [
19459                         {
19460                             tag: 'button',
19461                             type: 'button',
19462                             cls: 'btn btn-primary period',
19463                             html: 'AM'
19464                             
19465                         }
19466                     ]
19467                 }
19468             ]
19469         });
19470         
19471         time.createChild({
19472             tag: 'tr',
19473             cn: [
19474                 {
19475                     tag: 'td',
19476                     cn: [
19477                         {
19478                             tag: 'a',
19479                             href: '#',
19480                             cls: 'btn',
19481                             cn: [
19482                                 {
19483                                     tag: 'span',
19484                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19485                                 }
19486                             ]
19487                         }
19488                     ]
19489                 },
19490                 {
19491                     tag: 'td',
19492                     cls: 'separator'
19493                 },
19494                 {
19495                     tag: 'td',
19496                     cn: [
19497                         {
19498                             tag: 'a',
19499                             href: '#',
19500                             cls: 'btn',
19501                             cn: [
19502                                 {
19503                                     tag: 'span',
19504                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19505                                 }
19506                             ]
19507                         }
19508                     ]
19509                 },
19510                 {
19511                     tag: 'td',
19512                     cls: 'separator'
19513                 }
19514             ]
19515         });
19516         
19517     },
19518     
19519     update: function()
19520     {
19521         
19522         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19523         
19524         this.fill();
19525     },
19526     
19527     fill: function() 
19528     {
19529         var hours = this.time.getHours();
19530         var minutes = this.time.getMinutes();
19531         var period = 'AM';
19532         
19533         if(hours > 11){
19534             period = 'PM';
19535         }
19536         
19537         if(hours == 0){
19538             hours = 12;
19539         }
19540         
19541         
19542         if(hours > 12){
19543             hours = hours - 12;
19544         }
19545         
19546         if(hours < 10){
19547             hours = '0' + hours;
19548         }
19549         
19550         if(minutes < 10){
19551             minutes = '0' + minutes;
19552         }
19553         
19554         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19555         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19556         this.pop.select('button', true).first().dom.innerHTML = period;
19557         
19558     },
19559     
19560     place: function()
19561     {   
19562         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19563         
19564         var cls = ['bottom'];
19565         
19566         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19567             cls.pop();
19568             cls.push('top');
19569         }
19570         
19571         cls.push('right');
19572         
19573         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19574             cls.pop();
19575             cls.push('left');
19576         }
19577         
19578         this.picker().addClass(cls.join('-'));
19579         
19580         var _this = this;
19581         
19582         Roo.each(cls, function(c){
19583             if(c == 'bottom'){
19584                 _this.picker().setTop(_this.inputEl().getHeight());
19585                 return;
19586             }
19587             if(c == 'top'){
19588                 _this.picker().setTop(0 - _this.picker().getHeight());
19589                 return;
19590             }
19591             
19592             if(c == 'left'){
19593                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19594                 return;
19595             }
19596             if(c == 'right'){
19597                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19598                 return;
19599             }
19600         });
19601         
19602     },
19603   
19604     onFocus : function()
19605     {
19606         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19607         this.show();
19608     },
19609     
19610     onBlur : function()
19611     {
19612         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19613         this.hide();
19614     },
19615     
19616     show : function()
19617     {
19618         this.picker().show();
19619         this.pop.show();
19620         this.update();
19621         this.place();
19622         
19623         this.fireEvent('show', this, this.date);
19624     },
19625     
19626     hide : function()
19627     {
19628         this.picker().hide();
19629         this.pop.hide();
19630         
19631         this.fireEvent('hide', this, this.date);
19632     },
19633     
19634     setTime : function()
19635     {
19636         this.hide();
19637         this.setValue(this.time.format(this.format));
19638         
19639         this.fireEvent('select', this, this.date);
19640         
19641         
19642     },
19643     
19644     onMousedown: function(e){
19645         e.stopPropagation();
19646         e.preventDefault();
19647     },
19648     
19649     onIncrementHours: function()
19650     {
19651         Roo.log('onIncrementHours');
19652         this.time = this.time.add(Date.HOUR, 1);
19653         this.update();
19654         
19655     },
19656     
19657     onDecrementHours: function()
19658     {
19659         Roo.log('onDecrementHours');
19660         this.time = this.time.add(Date.HOUR, -1);
19661         this.update();
19662     },
19663     
19664     onIncrementMinutes: function()
19665     {
19666         Roo.log('onIncrementMinutes');
19667         this.time = this.time.add(Date.MINUTE, 1);
19668         this.update();
19669     },
19670     
19671     onDecrementMinutes: function()
19672     {
19673         Roo.log('onDecrementMinutes');
19674         this.time = this.time.add(Date.MINUTE, -1);
19675         this.update();
19676     },
19677     
19678     onTogglePeriod: function()
19679     {
19680         Roo.log('onTogglePeriod');
19681         this.time = this.time.add(Date.HOUR, 12);
19682         this.update();
19683     }
19684     
19685    
19686 });
19687
19688 Roo.apply(Roo.bootstrap.TimeField,  {
19689     
19690     content : {
19691         tag: 'tbody',
19692         cn: [
19693             {
19694                 tag: 'tr',
19695                 cn: [
19696                 {
19697                     tag: 'td',
19698                     colspan: '7'
19699                 }
19700                 ]
19701             }
19702         ]
19703     },
19704     
19705     footer : {
19706         tag: 'tfoot',
19707         cn: [
19708             {
19709                 tag: 'tr',
19710                 cn: [
19711                 {
19712                     tag: 'th',
19713                     colspan: '7',
19714                     cls: '',
19715                     cn: [
19716                         {
19717                             tag: 'button',
19718                             cls: 'btn btn-info ok',
19719                             html: 'OK'
19720                         }
19721                     ]
19722                 }
19723
19724                 ]
19725             }
19726         ]
19727     }
19728 });
19729
19730 Roo.apply(Roo.bootstrap.TimeField,  {
19731   
19732     template : {
19733         tag: 'div',
19734         cls: 'datepicker dropdown-menu',
19735         cn: [
19736             {
19737                 tag: 'div',
19738                 cls: 'datepicker-time',
19739                 cn: [
19740                 {
19741                     tag: 'table',
19742                     cls: 'table-condensed',
19743                     cn:[
19744                     Roo.bootstrap.TimeField.content,
19745                     Roo.bootstrap.TimeField.footer
19746                     ]
19747                 }
19748                 ]
19749             }
19750         ]
19751     }
19752 });
19753
19754  
19755
19756  /*
19757  * - LGPL
19758  *
19759  * MonthField
19760  * 
19761  */
19762
19763 /**
19764  * @class Roo.bootstrap.MonthField
19765  * @extends Roo.bootstrap.Input
19766  * Bootstrap MonthField class
19767  * 
19768  * @cfg {String} language default en
19769  * 
19770  * @constructor
19771  * Create a new MonthField
19772  * @param {Object} config The config object
19773  */
19774
19775 Roo.bootstrap.MonthField = function(config){
19776     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19777     
19778     this.addEvents({
19779         /**
19780          * @event show
19781          * Fires when this field show.
19782          * @param {Roo.bootstrap.MonthField} this
19783          * @param {Mixed} date The date value
19784          */
19785         show : true,
19786         /**
19787          * @event show
19788          * Fires when this field hide.
19789          * @param {Roo.bootstrap.MonthField} this
19790          * @param {Mixed} date The date value
19791          */
19792         hide : true,
19793         /**
19794          * @event select
19795          * Fires when select a date.
19796          * @param {Roo.bootstrap.MonthField} this
19797          * @param {String} oldvalue The old value
19798          * @param {String} newvalue The new value
19799          */
19800         select : true
19801     });
19802 };
19803
19804 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19805     
19806     onRender: function(ct, position)
19807     {
19808         
19809         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19810         
19811         this.language = this.language || 'en';
19812         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19813         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19814         
19815         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19816         this.isInline = false;
19817         this.isInput = true;
19818         this.component = this.el.select('.add-on', true).first() || false;
19819         this.component = (this.component && this.component.length === 0) ? false : this.component;
19820         this.hasInput = this.component && this.inputEL().length;
19821         
19822         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19823         
19824         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19825         
19826         this.picker().on('mousedown', this.onMousedown, this);
19827         this.picker().on('click', this.onClick, this);
19828         
19829         this.picker().addClass('datepicker-dropdown');
19830         
19831         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19832             v.setStyle('width', '189px');
19833         });
19834         
19835         this.fillMonths();
19836         
19837         this.update();
19838         
19839         if(this.isInline) {
19840             this.show();
19841         }
19842         
19843     },
19844     
19845     setValue: function(v, suppressEvent)
19846     {   
19847         var o = this.getValue();
19848         
19849         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19850         
19851         this.update();
19852
19853         if(suppressEvent !== true){
19854             this.fireEvent('select', this, o, v);
19855         }
19856         
19857     },
19858     
19859     getValue: function()
19860     {
19861         return this.value;
19862     },
19863     
19864     onClick: function(e) 
19865     {
19866         e.stopPropagation();
19867         e.preventDefault();
19868         
19869         var target = e.getTarget();
19870         
19871         if(target.nodeName.toLowerCase() === 'i'){
19872             target = Roo.get(target).dom.parentNode;
19873         }
19874         
19875         var nodeName = target.nodeName;
19876         var className = target.className;
19877         var html = target.innerHTML;
19878         
19879         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19880             return;
19881         }
19882         
19883         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19884         
19885         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19886         
19887         this.hide();
19888                         
19889     },
19890     
19891     picker : function()
19892     {
19893         return this.pickerEl;
19894     },
19895     
19896     fillMonths: function()
19897     {    
19898         var i = 0;
19899         var months = this.picker().select('>.datepicker-months td', true).first();
19900         
19901         months.dom.innerHTML = '';
19902         
19903         while (i < 12) {
19904             var month = {
19905                 tag: 'span',
19906                 cls: 'month',
19907                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19908             };
19909             
19910             months.createChild(month);
19911         }
19912         
19913     },
19914     
19915     update: function()
19916     {
19917         var _this = this;
19918         
19919         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19920             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19921         }
19922         
19923         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19924             e.removeClass('active');
19925             
19926             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19927                 e.addClass('active');
19928             }
19929         })
19930     },
19931     
19932     place: function()
19933     {
19934         if(this.isInline) {
19935             return;
19936         }
19937         
19938         this.picker().removeClass(['bottom', 'top']);
19939         
19940         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19941             /*
19942              * place to the top of element!
19943              *
19944              */
19945             
19946             this.picker().addClass('top');
19947             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19948             
19949             return;
19950         }
19951         
19952         this.picker().addClass('bottom');
19953         
19954         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19955     },
19956     
19957     onFocus : function()
19958     {
19959         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19960         this.show();
19961     },
19962     
19963     onBlur : function()
19964     {
19965         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19966         
19967         var d = this.inputEl().getValue();
19968         
19969         this.setValue(d);
19970                 
19971         this.hide();
19972     },
19973     
19974     show : function()
19975     {
19976         this.picker().show();
19977         this.picker().select('>.datepicker-months', true).first().show();
19978         this.update();
19979         this.place();
19980         
19981         this.fireEvent('show', this, this.date);
19982     },
19983     
19984     hide : function()
19985     {
19986         if(this.isInline) {
19987             return;
19988         }
19989         this.picker().hide();
19990         this.fireEvent('hide', this, this.date);
19991         
19992     },
19993     
19994     onMousedown: function(e)
19995     {
19996         e.stopPropagation();
19997         e.preventDefault();
19998     },
19999     
20000     keyup: function(e)
20001     {
20002         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20003         this.update();
20004     },
20005
20006     fireKey: function(e)
20007     {
20008         if (!this.picker().isVisible()){
20009             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20010                 this.show();
20011             }
20012             return;
20013         }
20014         
20015         var dir;
20016         
20017         switch(e.keyCode){
20018             case 27: // escape
20019                 this.hide();
20020                 e.preventDefault();
20021                 break;
20022             case 37: // left
20023             case 39: // right
20024                 dir = e.keyCode == 37 ? -1 : 1;
20025                 
20026                 this.vIndex = this.vIndex + dir;
20027                 
20028                 if(this.vIndex < 0){
20029                     this.vIndex = 0;
20030                 }
20031                 
20032                 if(this.vIndex > 11){
20033                     this.vIndex = 11;
20034                 }
20035                 
20036                 if(isNaN(this.vIndex)){
20037                     this.vIndex = 0;
20038                 }
20039                 
20040                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20041                 
20042                 break;
20043             case 38: // up
20044             case 40: // down
20045                 
20046                 dir = e.keyCode == 38 ? -1 : 1;
20047                 
20048                 this.vIndex = this.vIndex + dir * 4;
20049                 
20050                 if(this.vIndex < 0){
20051                     this.vIndex = 0;
20052                 }
20053                 
20054                 if(this.vIndex > 11){
20055                     this.vIndex = 11;
20056                 }
20057                 
20058                 if(isNaN(this.vIndex)){
20059                     this.vIndex = 0;
20060                 }
20061                 
20062                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20063                 break;
20064                 
20065             case 13: // enter
20066                 
20067                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20068                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20069                 }
20070                 
20071                 this.hide();
20072                 e.preventDefault();
20073                 break;
20074             case 9: // tab
20075                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20076                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20077                 }
20078                 this.hide();
20079                 break;
20080             case 16: // shift
20081             case 17: // ctrl
20082             case 18: // alt
20083                 break;
20084             default :
20085                 this.hide();
20086                 
20087         }
20088     },
20089     
20090     remove: function() 
20091     {
20092         this.picker().remove();
20093     }
20094    
20095 });
20096
20097 Roo.apply(Roo.bootstrap.MonthField,  {
20098     
20099     content : {
20100         tag: 'tbody',
20101         cn: [
20102         {
20103             tag: 'tr',
20104             cn: [
20105             {
20106                 tag: 'td',
20107                 colspan: '7'
20108             }
20109             ]
20110         }
20111         ]
20112     },
20113     
20114     dates:{
20115         en: {
20116             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20117             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20118         }
20119     }
20120 });
20121
20122 Roo.apply(Roo.bootstrap.MonthField,  {
20123   
20124     template : {
20125         tag: 'div',
20126         cls: 'datepicker dropdown-menu roo-dynamic',
20127         cn: [
20128             {
20129                 tag: 'div',
20130                 cls: 'datepicker-months',
20131                 cn: [
20132                 {
20133                     tag: 'table',
20134                     cls: 'table-condensed',
20135                     cn:[
20136                         Roo.bootstrap.DateField.content
20137                     ]
20138                 }
20139                 ]
20140             }
20141         ]
20142     }
20143 });
20144
20145  
20146
20147  
20148  /*
20149  * - LGPL
20150  *
20151  * CheckBox
20152  * 
20153  */
20154
20155 /**
20156  * @class Roo.bootstrap.CheckBox
20157  * @extends Roo.bootstrap.Input
20158  * Bootstrap CheckBox class
20159  * 
20160  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20161  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20162  * @cfg {String} boxLabel The text that appears beside the checkbox
20163  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20164  * @cfg {Boolean} checked initnal the element
20165  * @cfg {Boolean} inline inline the element (default false)
20166  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20167  * @cfg {String} tooltip label tooltip
20168  * 
20169  * @constructor
20170  * Create a new CheckBox
20171  * @param {Object} config The config object
20172  */
20173
20174 Roo.bootstrap.CheckBox = function(config){
20175     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20176    
20177     this.addEvents({
20178         /**
20179         * @event check
20180         * Fires when the element is checked or unchecked.
20181         * @param {Roo.bootstrap.CheckBox} this This input
20182         * @param {Boolean} checked The new checked value
20183         */
20184        check : true,
20185        /**
20186         * @event click
20187         * Fires when the element is click.
20188         * @param {Roo.bootstrap.CheckBox} this This input
20189         */
20190        click : true
20191     });
20192     
20193 };
20194
20195 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20196   
20197     inputType: 'checkbox',
20198     inputValue: 1,
20199     valueOff: 0,
20200     boxLabel: false,
20201     checked: false,
20202     weight : false,
20203     inline: false,
20204     tooltip : '',
20205     
20206     getAutoCreate : function()
20207     {
20208         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20209         
20210         var id = Roo.id();
20211         
20212         var cfg = {};
20213         
20214         cfg.cls = 'form-group ' + this.inputType; //input-group
20215         
20216         if(this.inline){
20217             cfg.cls += ' ' + this.inputType + '-inline';
20218         }
20219         
20220         var input =  {
20221             tag: 'input',
20222             id : id,
20223             type : this.inputType,
20224             value : this.inputValue,
20225             cls : 'roo-' + this.inputType, //'form-box',
20226             placeholder : this.placeholder || ''
20227             
20228         };
20229         
20230         if(this.inputType != 'radio'){
20231             var hidden =  {
20232                 tag: 'input',
20233                 type : 'hidden',
20234                 cls : 'roo-hidden-value',
20235                 value : this.checked ? this.inputValue : this.valueOff
20236             };
20237         }
20238         
20239             
20240         if (this.weight) { // Validity check?
20241             cfg.cls += " " + this.inputType + "-" + this.weight;
20242         }
20243         
20244         if (this.disabled) {
20245             input.disabled=true;
20246         }
20247         
20248         if(this.checked){
20249             input.checked = this.checked;
20250         }
20251         
20252         if (this.name) {
20253             
20254             input.name = this.name;
20255             
20256             if(this.inputType != 'radio'){
20257                 hidden.name = this.name;
20258                 input.name = '_hidden_' + this.name;
20259             }
20260         }
20261         
20262         if (this.size) {
20263             input.cls += ' input-' + this.size;
20264         }
20265         
20266         var settings=this;
20267         
20268         ['xs','sm','md','lg'].map(function(size){
20269             if (settings[size]) {
20270                 cfg.cls += ' col-' + size + '-' + settings[size];
20271             }
20272         });
20273         
20274         var inputblock = input;
20275          
20276         if (this.before || this.after) {
20277             
20278             inputblock = {
20279                 cls : 'input-group',
20280                 cn :  [] 
20281             };
20282             
20283             if (this.before) {
20284                 inputblock.cn.push({
20285                     tag :'span',
20286                     cls : 'input-group-addon',
20287                     html : this.before
20288                 });
20289             }
20290             
20291             inputblock.cn.push(input);
20292             
20293             if(this.inputType != 'radio'){
20294                 inputblock.cn.push(hidden);
20295             }
20296             
20297             if (this.after) {
20298                 inputblock.cn.push({
20299                     tag :'span',
20300                     cls : 'input-group-addon',
20301                     html : this.after
20302                 });
20303             }
20304             
20305         }
20306         
20307         if (align ==='left' && this.fieldLabel.length) {
20308 //                Roo.log("left and has label");
20309             cfg.cn = [
20310                 {
20311                     tag: 'label',
20312                     'for' :  id,
20313                     cls : 'control-label',
20314                     html : this.fieldLabel
20315                 },
20316                 {
20317                     cls : "", 
20318                     cn: [
20319                         inputblock
20320                     ]
20321                 }
20322             ];
20323             
20324             if(this.labelWidth > 12){
20325                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20326             }
20327             
20328             if(this.labelWidth < 13 && this.labelmd == 0){
20329                 this.labelmd = this.labelWidth;
20330             }
20331             
20332             if(this.labellg > 0){
20333                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20334                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20335             }
20336             
20337             if(this.labelmd > 0){
20338                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20339                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20340             }
20341             
20342             if(this.labelsm > 0){
20343                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20344                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20345             }
20346             
20347             if(this.labelxs > 0){
20348                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20349                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20350             }
20351             
20352         } else if ( this.fieldLabel.length) {
20353 //                Roo.log(" label");
20354                 cfg.cn = [
20355                    
20356                     {
20357                         tag: this.boxLabel ? 'span' : 'label',
20358                         'for': id,
20359                         cls: 'control-label box-input-label',
20360                         //cls : 'input-group-addon',
20361                         html : this.fieldLabel
20362                     },
20363                     
20364                     inputblock
20365                     
20366                 ];
20367
20368         } else {
20369             
20370 //                Roo.log(" no label && no align");
20371                 cfg.cn = [  inputblock ] ;
20372                 
20373                 
20374         }
20375         
20376         if(this.boxLabel){
20377              var boxLabelCfg = {
20378                 tag: 'label',
20379                 //'for': id, // box label is handled by onclick - so no for...
20380                 cls: 'box-label',
20381                 html: this.boxLabel
20382             };
20383             
20384             if(this.tooltip){
20385                 boxLabelCfg.tooltip = this.tooltip;
20386             }
20387              
20388             cfg.cn.push(boxLabelCfg);
20389         }
20390         
20391         if(this.inputType != 'radio'){
20392             cfg.cn.push(hidden);
20393         }
20394         
20395         return cfg;
20396         
20397     },
20398     
20399     /**
20400      * return the real input element.
20401      */
20402     inputEl: function ()
20403     {
20404         return this.el.select('input.roo-' + this.inputType,true).first();
20405     },
20406     hiddenEl: function ()
20407     {
20408         return this.el.select('input.roo-hidden-value',true).first();
20409     },
20410     
20411     labelEl: function()
20412     {
20413         return this.el.select('label.control-label',true).first();
20414     },
20415     /* depricated... */
20416     
20417     label: function()
20418     {
20419         return this.labelEl();
20420     },
20421     
20422     boxLabelEl: function()
20423     {
20424         return this.el.select('label.box-label',true).first();
20425     },
20426     
20427     initEvents : function()
20428     {
20429 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20430         
20431         this.inputEl().on('click', this.onClick,  this);
20432         
20433         if (this.boxLabel) { 
20434             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20435         }
20436         
20437         this.startValue = this.getValue();
20438         
20439         if(this.groupId){
20440             Roo.bootstrap.CheckBox.register(this);
20441         }
20442     },
20443     
20444     onClick : function(e)
20445     {   
20446         if(this.fireEvent('click', this, e) !== false){
20447             this.setChecked(!this.checked);
20448         }
20449         
20450     },
20451     
20452     setChecked : function(state,suppressEvent)
20453     {
20454         this.startValue = this.getValue();
20455
20456         if(this.inputType == 'radio'){
20457             
20458             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20459                 e.dom.checked = false;
20460             });
20461             
20462             this.inputEl().dom.checked = true;
20463             
20464             this.inputEl().dom.value = this.inputValue;
20465             
20466             if(suppressEvent !== true){
20467                 this.fireEvent('check', this, true);
20468             }
20469             
20470             this.validate();
20471             
20472             return;
20473         }
20474         
20475         this.checked = state;
20476         
20477         this.inputEl().dom.checked = state;
20478         
20479         
20480         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20481         
20482         if(suppressEvent !== true){
20483             this.fireEvent('check', this, state);
20484         }
20485         
20486         this.validate();
20487     },
20488     
20489     getValue : function()
20490     {
20491         if(this.inputType == 'radio'){
20492             return this.getGroupValue();
20493         }
20494         
20495         return this.hiddenEl().dom.value;
20496         
20497     },
20498     
20499     getGroupValue : function()
20500     {
20501         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20502             return '';
20503         }
20504         
20505         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20506     },
20507     
20508     setValue : function(v,suppressEvent)
20509     {
20510         if(this.inputType == 'radio'){
20511             this.setGroupValue(v, suppressEvent);
20512             return;
20513         }
20514         
20515         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20516         
20517         this.validate();
20518     },
20519     
20520     setGroupValue : function(v, suppressEvent)
20521     {
20522         this.startValue = this.getValue();
20523         
20524         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20525             e.dom.checked = false;
20526             
20527             if(e.dom.value == v){
20528                 e.dom.checked = true;
20529             }
20530         });
20531         
20532         if(suppressEvent !== true){
20533             this.fireEvent('check', this, true);
20534         }
20535
20536         this.validate();
20537         
20538         return;
20539     },
20540     
20541     validate : function()
20542     {
20543         if(
20544                 this.disabled || 
20545                 (this.inputType == 'radio' && this.validateRadio()) ||
20546                 (this.inputType == 'checkbox' && this.validateCheckbox())
20547         ){
20548             this.markValid();
20549             return true;
20550         }
20551         
20552         this.markInvalid();
20553         return false;
20554     },
20555     
20556     validateRadio : function()
20557     {
20558         if(this.allowBlank){
20559             return true;
20560         }
20561         
20562         var valid = false;
20563         
20564         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20565             if(!e.dom.checked){
20566                 return;
20567             }
20568             
20569             valid = true;
20570             
20571             return false;
20572         });
20573         
20574         return valid;
20575     },
20576     
20577     validateCheckbox : function()
20578     {
20579         if(!this.groupId){
20580             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20581             //return (this.getValue() == this.inputValue) ? true : false;
20582         }
20583         
20584         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20585         
20586         if(!group){
20587             return false;
20588         }
20589         
20590         var r = false;
20591         
20592         for(var i in group){
20593             if(group[i].el.isVisible(true)){
20594                 r = false;
20595                 break;
20596             }
20597             
20598             r = true;
20599         }
20600         
20601         for(var i in group){
20602             if(r){
20603                 break;
20604             }
20605             
20606             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20607         }
20608         
20609         return r;
20610     },
20611     
20612     /**
20613      * Mark this field as valid
20614      */
20615     markValid : function()
20616     {
20617         var _this = this;
20618         
20619         this.fireEvent('valid', this);
20620         
20621         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20622         
20623         if(this.groupId){
20624             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20625         }
20626         
20627         if(label){
20628             label.markValid();
20629         }
20630
20631         if(this.inputType == 'radio'){
20632             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20633                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20634                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20635             });
20636             
20637             return;
20638         }
20639
20640         if(!this.groupId){
20641             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20642             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20643             return;
20644         }
20645         
20646         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20647         
20648         if(!group){
20649             return;
20650         }
20651         
20652         for(var i in group){
20653             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20654             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20655         }
20656     },
20657     
20658      /**
20659      * Mark this field as invalid
20660      * @param {String} msg The validation message
20661      */
20662     markInvalid : function(msg)
20663     {
20664         if(this.allowBlank){
20665             return;
20666         }
20667         
20668         var _this = this;
20669         
20670         this.fireEvent('invalid', this, msg);
20671         
20672         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20673         
20674         if(this.groupId){
20675             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20676         }
20677         
20678         if(label){
20679             label.markInvalid();
20680         }
20681             
20682         if(this.inputType == 'radio'){
20683             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20684                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20685                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20686             });
20687             
20688             return;
20689         }
20690         
20691         if(!this.groupId){
20692             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20693             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20694             return;
20695         }
20696         
20697         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20698         
20699         if(!group){
20700             return;
20701         }
20702         
20703         for(var i in group){
20704             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20705             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20706         }
20707         
20708     },
20709     
20710     clearInvalid : function()
20711     {
20712         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20713         
20714         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20715         
20716         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20717         
20718         if (label && label.iconEl) {
20719             label.iconEl.removeClass(label.validClass);
20720             label.iconEl.removeClass(label.invalidClass);
20721         }
20722     },
20723     
20724     disable : function()
20725     {
20726         if(this.inputType != 'radio'){
20727             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20728             return;
20729         }
20730         
20731         var _this = this;
20732         
20733         if(this.rendered){
20734             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20735                 _this.getActionEl().addClass(this.disabledClass);
20736                 e.dom.disabled = true;
20737             });
20738         }
20739         
20740         this.disabled = true;
20741         this.fireEvent("disable", this);
20742         return this;
20743     },
20744
20745     enable : function()
20746     {
20747         if(this.inputType != 'radio'){
20748             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20749             return;
20750         }
20751         
20752         var _this = this;
20753         
20754         if(this.rendered){
20755             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20756                 _this.getActionEl().removeClass(this.disabledClass);
20757                 e.dom.disabled = false;
20758             });
20759         }
20760         
20761         this.disabled = false;
20762         this.fireEvent("enable", this);
20763         return this;
20764     },
20765     
20766     setBoxLabel : function(v)
20767     {
20768         this.boxLabel = v;
20769         
20770         if(this.rendered){
20771             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20772         }
20773     }
20774
20775 });
20776
20777 Roo.apply(Roo.bootstrap.CheckBox, {
20778     
20779     groups: {},
20780     
20781      /**
20782     * register a CheckBox Group
20783     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20784     */
20785     register : function(checkbox)
20786     {
20787         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20788             this.groups[checkbox.groupId] = {};
20789         }
20790         
20791         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20792             return;
20793         }
20794         
20795         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20796         
20797     },
20798     /**
20799     * fetch a CheckBox Group based on the group ID
20800     * @param {string} the group ID
20801     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20802     */
20803     get: function(groupId) {
20804         if (typeof(this.groups[groupId]) == 'undefined') {
20805             return false;
20806         }
20807         
20808         return this.groups[groupId] ;
20809     }
20810     
20811     
20812 });
20813 /*
20814  * - LGPL
20815  *
20816  * RadioItem
20817  * 
20818  */
20819
20820 /**
20821  * @class Roo.bootstrap.Radio
20822  * @extends Roo.bootstrap.Component
20823  * Bootstrap Radio class
20824  * @cfg {String} boxLabel - the label associated
20825  * @cfg {String} value - the value of radio
20826  * 
20827  * @constructor
20828  * Create a new Radio
20829  * @param {Object} config The config object
20830  */
20831 Roo.bootstrap.Radio = function(config){
20832     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20833     
20834 };
20835
20836 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20837     
20838     boxLabel : '',
20839     
20840     value : '',
20841     
20842     getAutoCreate : function()
20843     {
20844         var cfg = {
20845             tag : 'div',
20846             cls : 'form-group radio',
20847             cn : [
20848                 {
20849                     tag : 'label',
20850                     cls : 'box-label',
20851                     html : this.boxLabel
20852                 }
20853             ]
20854         };
20855         
20856         return cfg;
20857     },
20858     
20859     initEvents : function() 
20860     {
20861         this.parent().register(this);
20862         
20863         this.el.on('click', this.onClick, this);
20864         
20865     },
20866     
20867     onClick : function()
20868     {
20869         this.setChecked(true);
20870     },
20871     
20872     setChecked : function(state, suppressEvent)
20873     {
20874         this.parent().setValue(this.value, suppressEvent);
20875         
20876     },
20877     
20878     setBoxLabel : function(v)
20879     {
20880         this.boxLabel = v;
20881         
20882         if(this.rendered){
20883             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20884         }
20885     }
20886     
20887 });
20888  
20889
20890  /*
20891  * - LGPL
20892  *
20893  * Input
20894  * 
20895  */
20896
20897 /**
20898  * @class Roo.bootstrap.SecurePass
20899  * @extends Roo.bootstrap.Input
20900  * Bootstrap SecurePass class
20901  *
20902  * 
20903  * @constructor
20904  * Create a new SecurePass
20905  * @param {Object} config The config object
20906  */
20907  
20908 Roo.bootstrap.SecurePass = function (config) {
20909     // these go here, so the translation tool can replace them..
20910     this.errors = {
20911         PwdEmpty: "Please type a password, and then retype it to confirm.",
20912         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20913         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20914         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20915         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20916         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20917         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20918         TooWeak: "Your password is Too Weak."
20919     },
20920     this.meterLabel = "Password strength:";
20921     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20922     this.meterClass = [
20923         "roo-password-meter-tooweak", 
20924         "roo-password-meter-weak", 
20925         "roo-password-meter-medium", 
20926         "roo-password-meter-strong", 
20927         "roo-password-meter-grey"
20928     ];
20929     
20930     this.errors = {};
20931     
20932     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20933 }
20934
20935 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20936     /**
20937      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20938      * {
20939      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20940      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20941      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20942      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20943      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20944      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20945      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20946      * })
20947      */
20948     // private
20949     
20950     meterWidth: 300,
20951     errorMsg :'',    
20952     errors: false,
20953     imageRoot: '/',
20954     /**
20955      * @cfg {String/Object} Label for the strength meter (defaults to
20956      * 'Password strength:')
20957      */
20958     // private
20959     meterLabel: '',
20960     /**
20961      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20962      * ['Weak', 'Medium', 'Strong'])
20963      */
20964     // private    
20965     pwdStrengths: false,    
20966     // private
20967     strength: 0,
20968     // private
20969     _lastPwd: null,
20970     // private
20971     kCapitalLetter: 0,
20972     kSmallLetter: 1,
20973     kDigit: 2,
20974     kPunctuation: 3,
20975     
20976     insecure: false,
20977     // private
20978     initEvents: function ()
20979     {
20980         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20981
20982         if (this.el.is('input[type=password]') && Roo.isSafari) {
20983             this.el.on('keydown', this.SafariOnKeyDown, this);
20984         }
20985
20986         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20987     },
20988     // private
20989     onRender: function (ct, position)
20990     {
20991         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20992         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20993         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20994
20995         this.trigger.createChild({
20996                    cn: [
20997                     {
20998                     //id: 'PwdMeter',
20999                     tag: 'div',
21000                     cls: 'roo-password-meter-grey col-xs-12',
21001                     style: {
21002                         //width: 0,
21003                         //width: this.meterWidth + 'px'                                                
21004                         }
21005                     },
21006                     {                            
21007                          cls: 'roo-password-meter-text'                          
21008                     }
21009                 ]            
21010         });
21011
21012          
21013         if (this.hideTrigger) {
21014             this.trigger.setDisplayed(false);
21015         }
21016         this.setSize(this.width || '', this.height || '');
21017     },
21018     // private
21019     onDestroy: function ()
21020     {
21021         if (this.trigger) {
21022             this.trigger.removeAllListeners();
21023             this.trigger.remove();
21024         }
21025         if (this.wrap) {
21026             this.wrap.remove();
21027         }
21028         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21029     },
21030     // private
21031     checkStrength: function ()
21032     {
21033         var pwd = this.inputEl().getValue();
21034         if (pwd == this._lastPwd) {
21035             return;
21036         }
21037
21038         var strength;
21039         if (this.ClientSideStrongPassword(pwd)) {
21040             strength = 3;
21041         } else if (this.ClientSideMediumPassword(pwd)) {
21042             strength = 2;
21043         } else if (this.ClientSideWeakPassword(pwd)) {
21044             strength = 1;
21045         } else {
21046             strength = 0;
21047         }
21048         
21049         Roo.log('strength1: ' + strength);
21050         
21051         //var pm = this.trigger.child('div/div/div').dom;
21052         var pm = this.trigger.child('div/div');
21053         pm.removeClass(this.meterClass);
21054         pm.addClass(this.meterClass[strength]);
21055                 
21056         
21057         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21058                 
21059         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21060         
21061         this._lastPwd = pwd;
21062     },
21063     reset: function ()
21064     {
21065         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21066         
21067         this._lastPwd = '';
21068         
21069         var pm = this.trigger.child('div/div');
21070         pm.removeClass(this.meterClass);
21071         pm.addClass('roo-password-meter-grey');        
21072         
21073         
21074         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21075         
21076         pt.innerHTML = '';
21077         this.inputEl().dom.type='password';
21078     },
21079     // private
21080     validateValue: function (value)
21081     {
21082         
21083         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21084             return false;
21085         }
21086         if (value.length == 0) {
21087             if (this.allowBlank) {
21088                 this.clearInvalid();
21089                 return true;
21090             }
21091
21092             this.markInvalid(this.errors.PwdEmpty);
21093             this.errorMsg = this.errors.PwdEmpty;
21094             return false;
21095         }
21096         
21097         if(this.insecure){
21098             return true;
21099         }
21100         
21101         if ('[\x21-\x7e]*'.match(value)) {
21102             this.markInvalid(this.errors.PwdBadChar);
21103             this.errorMsg = this.errors.PwdBadChar;
21104             return false;
21105         }
21106         if (value.length < 6) {
21107             this.markInvalid(this.errors.PwdShort);
21108             this.errorMsg = this.errors.PwdShort;
21109             return false;
21110         }
21111         if (value.length > 16) {
21112             this.markInvalid(this.errors.PwdLong);
21113             this.errorMsg = this.errors.PwdLong;
21114             return false;
21115         }
21116         var strength;
21117         if (this.ClientSideStrongPassword(value)) {
21118             strength = 3;
21119         } else if (this.ClientSideMediumPassword(value)) {
21120             strength = 2;
21121         } else if (this.ClientSideWeakPassword(value)) {
21122             strength = 1;
21123         } else {
21124             strength = 0;
21125         }
21126
21127         
21128         if (strength < 2) {
21129             //this.markInvalid(this.errors.TooWeak);
21130             this.errorMsg = this.errors.TooWeak;
21131             //return false;
21132         }
21133         
21134         
21135         console.log('strength2: ' + strength);
21136         
21137         //var pm = this.trigger.child('div/div/div').dom;
21138         
21139         var pm = this.trigger.child('div/div');
21140         pm.removeClass(this.meterClass);
21141         pm.addClass(this.meterClass[strength]);
21142                 
21143         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21144                 
21145         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21146         
21147         this.errorMsg = ''; 
21148         return true;
21149     },
21150     // private
21151     CharacterSetChecks: function (type)
21152     {
21153         this.type = type;
21154         this.fResult = false;
21155     },
21156     // private
21157     isctype: function (character, type)
21158     {
21159         switch (type) {  
21160             case this.kCapitalLetter:
21161                 if (character >= 'A' && character <= 'Z') {
21162                     return true;
21163                 }
21164                 break;
21165             
21166             case this.kSmallLetter:
21167                 if (character >= 'a' && character <= 'z') {
21168                     return true;
21169                 }
21170                 break;
21171             
21172             case this.kDigit:
21173                 if (character >= '0' && character <= '9') {
21174                     return true;
21175                 }
21176                 break;
21177             
21178             case this.kPunctuation:
21179                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21180                     return true;
21181                 }
21182                 break;
21183             
21184             default:
21185                 return false;
21186         }
21187
21188     },
21189     // private
21190     IsLongEnough: function (pwd, size)
21191     {
21192         return !(pwd == null || isNaN(size) || pwd.length < size);
21193     },
21194     // private
21195     SpansEnoughCharacterSets: function (word, nb)
21196     {
21197         if (!this.IsLongEnough(word, nb))
21198         {
21199             return false;
21200         }
21201
21202         var characterSetChecks = new Array(
21203             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21204             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21205         );
21206         
21207         for (var index = 0; index < word.length; ++index) {
21208             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21209                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21210                     characterSetChecks[nCharSet].fResult = true;
21211                     break;
21212                 }
21213             }
21214         }
21215
21216         var nCharSets = 0;
21217         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21218             if (characterSetChecks[nCharSet].fResult) {
21219                 ++nCharSets;
21220             }
21221         }
21222
21223         if (nCharSets < nb) {
21224             return false;
21225         }
21226         return true;
21227     },
21228     // private
21229     ClientSideStrongPassword: function (pwd)
21230     {
21231         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21232     },
21233     // private
21234     ClientSideMediumPassword: function (pwd)
21235     {
21236         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21237     },
21238     // private
21239     ClientSideWeakPassword: function (pwd)
21240     {
21241         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21242     }
21243           
21244 })//<script type="text/javascript">
21245
21246 /*
21247  * Based  Ext JS Library 1.1.1
21248  * Copyright(c) 2006-2007, Ext JS, LLC.
21249  * LGPL
21250  *
21251  */
21252  
21253 /**
21254  * @class Roo.HtmlEditorCore
21255  * @extends Roo.Component
21256  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21257  *
21258  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21259  */
21260
21261 Roo.HtmlEditorCore = function(config){
21262     
21263     
21264     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21265     
21266     
21267     this.addEvents({
21268         /**
21269          * @event initialize
21270          * Fires when the editor is fully initialized (including the iframe)
21271          * @param {Roo.HtmlEditorCore} this
21272          */
21273         initialize: true,
21274         /**
21275          * @event activate
21276          * Fires when the editor is first receives the focus. Any insertion must wait
21277          * until after this event.
21278          * @param {Roo.HtmlEditorCore} this
21279          */
21280         activate: true,
21281          /**
21282          * @event beforesync
21283          * Fires before the textarea is updated with content from the editor iframe. Return false
21284          * to cancel the sync.
21285          * @param {Roo.HtmlEditorCore} this
21286          * @param {String} html
21287          */
21288         beforesync: true,
21289          /**
21290          * @event beforepush
21291          * Fires before the iframe editor is updated with content from the textarea. Return false
21292          * to cancel the push.
21293          * @param {Roo.HtmlEditorCore} this
21294          * @param {String} html
21295          */
21296         beforepush: true,
21297          /**
21298          * @event sync
21299          * Fires when the textarea is updated with content from the editor iframe.
21300          * @param {Roo.HtmlEditorCore} this
21301          * @param {String} html
21302          */
21303         sync: true,
21304          /**
21305          * @event push
21306          * Fires when the iframe editor is updated with content from the textarea.
21307          * @param {Roo.HtmlEditorCore} this
21308          * @param {String} html
21309          */
21310         push: true,
21311         
21312         /**
21313          * @event editorevent
21314          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21315          * @param {Roo.HtmlEditorCore} this
21316          */
21317         editorevent: true
21318         
21319     });
21320     
21321     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21322     
21323     // defaults : white / black...
21324     this.applyBlacklists();
21325     
21326     
21327     
21328 };
21329
21330
21331 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21332
21333
21334      /**
21335      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21336      */
21337     
21338     owner : false,
21339     
21340      /**
21341      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21342      *                        Roo.resizable.
21343      */
21344     resizable : false,
21345      /**
21346      * @cfg {Number} height (in pixels)
21347      */   
21348     height: 300,
21349    /**
21350      * @cfg {Number} width (in pixels)
21351      */   
21352     width: 500,
21353     
21354     /**
21355      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21356      * 
21357      */
21358     stylesheets: false,
21359     
21360     // id of frame..
21361     frameId: false,
21362     
21363     // private properties
21364     validationEvent : false,
21365     deferHeight: true,
21366     initialized : false,
21367     activated : false,
21368     sourceEditMode : false,
21369     onFocus : Roo.emptyFn,
21370     iframePad:3,
21371     hideMode:'offsets',
21372     
21373     clearUp: true,
21374     
21375     // blacklist + whitelisted elements..
21376     black: false,
21377     white: false,
21378      
21379     bodyCls : '',
21380
21381     /**
21382      * Protected method that will not generally be called directly. It
21383      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21384      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21385      */
21386     getDocMarkup : function(){
21387         // body styles..
21388         var st = '';
21389         
21390         // inherit styels from page...?? 
21391         if (this.stylesheets === false) {
21392             
21393             Roo.get(document.head).select('style').each(function(node) {
21394                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21395             });
21396             
21397             Roo.get(document.head).select('link').each(function(node) { 
21398                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21399             });
21400             
21401         } else if (!this.stylesheets.length) {
21402                 // simple..
21403                 st = '<style type="text/css">' +
21404                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21405                    '</style>';
21406         } else { 
21407             st = '<style type="text/css">' +
21408                     this.stylesheets +
21409                 '</style>';
21410         }
21411         
21412         st +=  '<style type="text/css">' +
21413             'IMG { cursor: pointer } ' +
21414         '</style>';
21415
21416         var cls = 'roo-htmleditor-body';
21417         
21418         if(this.bodyCls.length){
21419             cls += ' ' + this.bodyCls;
21420         }
21421         
21422         return '<html><head>' + st  +
21423             //<style type="text/css">' +
21424             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21425             //'</style>' +
21426             ' </head><body class="' +  cls + '"></body></html>';
21427     },
21428
21429     // private
21430     onRender : function(ct, position)
21431     {
21432         var _t = this;
21433         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21434         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21435         
21436         
21437         this.el.dom.style.border = '0 none';
21438         this.el.dom.setAttribute('tabIndex', -1);
21439         this.el.addClass('x-hidden hide');
21440         
21441         
21442         
21443         if(Roo.isIE){ // fix IE 1px bogus margin
21444             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21445         }
21446        
21447         
21448         this.frameId = Roo.id();
21449         
21450          
21451         
21452         var iframe = this.owner.wrap.createChild({
21453             tag: 'iframe',
21454             cls: 'form-control', // bootstrap..
21455             id: this.frameId,
21456             name: this.frameId,
21457             frameBorder : 'no',
21458             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21459         }, this.el
21460         );
21461         
21462         
21463         this.iframe = iframe.dom;
21464
21465          this.assignDocWin();
21466         
21467         this.doc.designMode = 'on';
21468        
21469         this.doc.open();
21470         this.doc.write(this.getDocMarkup());
21471         this.doc.close();
21472
21473         
21474         var task = { // must defer to wait for browser to be ready
21475             run : function(){
21476                 //console.log("run task?" + this.doc.readyState);
21477                 this.assignDocWin();
21478                 if(this.doc.body || this.doc.readyState == 'complete'){
21479                     try {
21480                         this.doc.designMode="on";
21481                     } catch (e) {
21482                         return;
21483                     }
21484                     Roo.TaskMgr.stop(task);
21485                     this.initEditor.defer(10, this);
21486                 }
21487             },
21488             interval : 10,
21489             duration: 10000,
21490             scope: this
21491         };
21492         Roo.TaskMgr.start(task);
21493
21494     },
21495
21496     // private
21497     onResize : function(w, h)
21498     {
21499          Roo.log('resize: ' +w + ',' + h );
21500         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21501         if(!this.iframe){
21502             return;
21503         }
21504         if(typeof w == 'number'){
21505             
21506             this.iframe.style.width = w + 'px';
21507         }
21508         if(typeof h == 'number'){
21509             
21510             this.iframe.style.height = h + 'px';
21511             if(this.doc){
21512                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21513             }
21514         }
21515         
21516     },
21517
21518     /**
21519      * Toggles the editor between standard and source edit mode.
21520      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21521      */
21522     toggleSourceEdit : function(sourceEditMode){
21523         
21524         this.sourceEditMode = sourceEditMode === true;
21525         
21526         if(this.sourceEditMode){
21527  
21528             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21529             
21530         }else{
21531             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21532             //this.iframe.className = '';
21533             this.deferFocus();
21534         }
21535         //this.setSize(this.owner.wrap.getSize());
21536         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21537     },
21538
21539     
21540   
21541
21542     /**
21543      * Protected method that will not generally be called directly. If you need/want
21544      * custom HTML cleanup, this is the method you should override.
21545      * @param {String} html The HTML to be cleaned
21546      * return {String} The cleaned HTML
21547      */
21548     cleanHtml : function(html){
21549         html = String(html);
21550         if(html.length > 5){
21551             if(Roo.isSafari){ // strip safari nonsense
21552                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21553             }
21554         }
21555         if(html == '&nbsp;'){
21556             html = '';
21557         }
21558         return html;
21559     },
21560
21561     /**
21562      * HTML Editor -> Textarea
21563      * Protected method that will not generally be called directly. Syncs the contents
21564      * of the editor iframe with the textarea.
21565      */
21566     syncValue : function(){
21567         if(this.initialized){
21568             var bd = (this.doc.body || this.doc.documentElement);
21569             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21570             var html = bd.innerHTML;
21571             if(Roo.isSafari){
21572                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21573                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21574                 if(m && m[1]){
21575                     html = '<div style="'+m[0]+'">' + html + '</div>';
21576                 }
21577             }
21578             html = this.cleanHtml(html);
21579             // fix up the special chars.. normaly like back quotes in word...
21580             // however we do not want to do this with chinese..
21581             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21582                 var cc = b.charCodeAt();
21583                 if (
21584                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21585                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21586                     (cc >= 0xf900 && cc < 0xfb00 )
21587                 ) {
21588                         return b;
21589                 }
21590                 return "&#"+cc+";" 
21591             });
21592             if(this.owner.fireEvent('beforesync', this, html) !== false){
21593                 this.el.dom.value = html;
21594                 this.owner.fireEvent('sync', this, html);
21595             }
21596         }
21597     },
21598
21599     /**
21600      * Protected method that will not generally be called directly. Pushes the value of the textarea
21601      * into the iframe editor.
21602      */
21603     pushValue : function(){
21604         if(this.initialized){
21605             var v = this.el.dom.value.trim();
21606             
21607 //            if(v.length < 1){
21608 //                v = '&#160;';
21609 //            }
21610             
21611             if(this.owner.fireEvent('beforepush', this, v) !== false){
21612                 var d = (this.doc.body || this.doc.documentElement);
21613                 d.innerHTML = v;
21614                 this.cleanUpPaste();
21615                 this.el.dom.value = d.innerHTML;
21616                 this.owner.fireEvent('push', this, v);
21617             }
21618         }
21619     },
21620
21621     // private
21622     deferFocus : function(){
21623         this.focus.defer(10, this);
21624     },
21625
21626     // doc'ed in Field
21627     focus : function(){
21628         if(this.win && !this.sourceEditMode){
21629             this.win.focus();
21630         }else{
21631             this.el.focus();
21632         }
21633     },
21634     
21635     assignDocWin: function()
21636     {
21637         var iframe = this.iframe;
21638         
21639          if(Roo.isIE){
21640             this.doc = iframe.contentWindow.document;
21641             this.win = iframe.contentWindow;
21642         } else {
21643 //            if (!Roo.get(this.frameId)) {
21644 //                return;
21645 //            }
21646 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21647 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21648             
21649             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21650                 return;
21651             }
21652             
21653             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21654             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21655         }
21656     },
21657     
21658     // private
21659     initEditor : function(){
21660         //console.log("INIT EDITOR");
21661         this.assignDocWin();
21662         
21663         
21664         
21665         this.doc.designMode="on";
21666         this.doc.open();
21667         this.doc.write(this.getDocMarkup());
21668         this.doc.close();
21669         
21670         var dbody = (this.doc.body || this.doc.documentElement);
21671         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21672         // this copies styles from the containing element into thsi one..
21673         // not sure why we need all of this..
21674         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21675         
21676         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21677         //ss['background-attachment'] = 'fixed'; // w3c
21678         dbody.bgProperties = 'fixed'; // ie
21679         //Roo.DomHelper.applyStyles(dbody, ss);
21680         Roo.EventManager.on(this.doc, {
21681             //'mousedown': this.onEditorEvent,
21682             'mouseup': this.onEditorEvent,
21683             'dblclick': this.onEditorEvent,
21684             'click': this.onEditorEvent,
21685             'keyup': this.onEditorEvent,
21686             buffer:100,
21687             scope: this
21688         });
21689         if(Roo.isGecko){
21690             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21691         }
21692         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21693             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21694         }
21695         this.initialized = true;
21696
21697         this.owner.fireEvent('initialize', this);
21698         this.pushValue();
21699     },
21700
21701     // private
21702     onDestroy : function(){
21703         
21704         
21705         
21706         if(this.rendered){
21707             
21708             //for (var i =0; i < this.toolbars.length;i++) {
21709             //    // fixme - ask toolbars for heights?
21710             //    this.toolbars[i].onDestroy();
21711            // }
21712             
21713             //this.wrap.dom.innerHTML = '';
21714             //this.wrap.remove();
21715         }
21716     },
21717
21718     // private
21719     onFirstFocus : function(){
21720         
21721         this.assignDocWin();
21722         
21723         
21724         this.activated = true;
21725          
21726     
21727         if(Roo.isGecko){ // prevent silly gecko errors
21728             this.win.focus();
21729             var s = this.win.getSelection();
21730             if(!s.focusNode || s.focusNode.nodeType != 3){
21731                 var r = s.getRangeAt(0);
21732                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21733                 r.collapse(true);
21734                 this.deferFocus();
21735             }
21736             try{
21737                 this.execCmd('useCSS', true);
21738                 this.execCmd('styleWithCSS', false);
21739             }catch(e){}
21740         }
21741         this.owner.fireEvent('activate', this);
21742     },
21743
21744     // private
21745     adjustFont: function(btn){
21746         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21747         //if(Roo.isSafari){ // safari
21748         //    adjust *= 2;
21749        // }
21750         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21751         if(Roo.isSafari){ // safari
21752             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21753             v =  (v < 10) ? 10 : v;
21754             v =  (v > 48) ? 48 : v;
21755             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21756             
21757         }
21758         
21759         
21760         v = Math.max(1, v+adjust);
21761         
21762         this.execCmd('FontSize', v  );
21763     },
21764
21765     onEditorEvent : function(e)
21766     {
21767         this.owner.fireEvent('editorevent', this, e);
21768       //  this.updateToolbar();
21769         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21770     },
21771
21772     insertTag : function(tg)
21773     {
21774         // could be a bit smarter... -> wrap the current selected tRoo..
21775         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21776             
21777             range = this.createRange(this.getSelection());
21778             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21779             wrappingNode.appendChild(range.extractContents());
21780             range.insertNode(wrappingNode);
21781
21782             return;
21783             
21784             
21785             
21786         }
21787         this.execCmd("formatblock",   tg);
21788         
21789     },
21790     
21791     insertText : function(txt)
21792     {
21793         
21794         
21795         var range = this.createRange();
21796         range.deleteContents();
21797                //alert(Sender.getAttribute('label'));
21798                
21799         range.insertNode(this.doc.createTextNode(txt));
21800     } ,
21801     
21802      
21803
21804     /**
21805      * Executes a Midas editor command on the editor document and performs necessary focus and
21806      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21807      * @param {String} cmd The Midas command
21808      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21809      */
21810     relayCmd : function(cmd, value){
21811         this.win.focus();
21812         this.execCmd(cmd, value);
21813         this.owner.fireEvent('editorevent', this);
21814         //this.updateToolbar();
21815         this.owner.deferFocus();
21816     },
21817
21818     /**
21819      * Executes a Midas editor command directly on the editor document.
21820      * For visual commands, you should use {@link #relayCmd} instead.
21821      * <b>This should only be called after the editor is initialized.</b>
21822      * @param {String} cmd The Midas command
21823      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21824      */
21825     execCmd : function(cmd, value){
21826         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21827         this.syncValue();
21828     },
21829  
21830  
21831    
21832     /**
21833      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21834      * to insert tRoo.
21835      * @param {String} text | dom node.. 
21836      */
21837     insertAtCursor : function(text)
21838     {
21839         
21840         if(!this.activated){
21841             return;
21842         }
21843         /*
21844         if(Roo.isIE){
21845             this.win.focus();
21846             var r = this.doc.selection.createRange();
21847             if(r){
21848                 r.collapse(true);
21849                 r.pasteHTML(text);
21850                 this.syncValue();
21851                 this.deferFocus();
21852             
21853             }
21854             return;
21855         }
21856         */
21857         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21858             this.win.focus();
21859             
21860             
21861             // from jquery ui (MIT licenced)
21862             var range, node;
21863             var win = this.win;
21864             
21865             if (win.getSelection && win.getSelection().getRangeAt) {
21866                 range = win.getSelection().getRangeAt(0);
21867                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21868                 range.insertNode(node);
21869             } else if (win.document.selection && win.document.selection.createRange) {
21870                 // no firefox support
21871                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21872                 win.document.selection.createRange().pasteHTML(txt);
21873             } else {
21874                 // no firefox support
21875                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21876                 this.execCmd('InsertHTML', txt);
21877             } 
21878             
21879             this.syncValue();
21880             
21881             this.deferFocus();
21882         }
21883     },
21884  // private
21885     mozKeyPress : function(e){
21886         if(e.ctrlKey){
21887             var c = e.getCharCode(), cmd;
21888           
21889             if(c > 0){
21890                 c = String.fromCharCode(c).toLowerCase();
21891                 switch(c){
21892                     case 'b':
21893                         cmd = 'bold';
21894                         break;
21895                     case 'i':
21896                         cmd = 'italic';
21897                         break;
21898                     
21899                     case 'u':
21900                         cmd = 'underline';
21901                         break;
21902                     
21903                     case 'v':
21904                         this.cleanUpPaste.defer(100, this);
21905                         return;
21906                         
21907                 }
21908                 if(cmd){
21909                     this.win.focus();
21910                     this.execCmd(cmd);
21911                     this.deferFocus();
21912                     e.preventDefault();
21913                 }
21914                 
21915             }
21916         }
21917     },
21918
21919     // private
21920     fixKeys : function(){ // load time branching for fastest keydown performance
21921         if(Roo.isIE){
21922             return function(e){
21923                 var k = e.getKey(), r;
21924                 if(k == e.TAB){
21925                     e.stopEvent();
21926                     r = this.doc.selection.createRange();
21927                     if(r){
21928                         r.collapse(true);
21929                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21930                         this.deferFocus();
21931                     }
21932                     return;
21933                 }
21934                 
21935                 if(k == e.ENTER){
21936                     r = this.doc.selection.createRange();
21937                     if(r){
21938                         var target = r.parentElement();
21939                         if(!target || target.tagName.toLowerCase() != 'li'){
21940                             e.stopEvent();
21941                             r.pasteHTML('<br />');
21942                             r.collapse(false);
21943                             r.select();
21944                         }
21945                     }
21946                 }
21947                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21948                     this.cleanUpPaste.defer(100, this);
21949                     return;
21950                 }
21951                 
21952                 
21953             };
21954         }else if(Roo.isOpera){
21955             return function(e){
21956                 var k = e.getKey();
21957                 if(k == e.TAB){
21958                     e.stopEvent();
21959                     this.win.focus();
21960                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21961                     this.deferFocus();
21962                 }
21963                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21964                     this.cleanUpPaste.defer(100, this);
21965                     return;
21966                 }
21967                 
21968             };
21969         }else if(Roo.isSafari){
21970             return function(e){
21971                 var k = e.getKey();
21972                 
21973                 if(k == e.TAB){
21974                     e.stopEvent();
21975                     this.execCmd('InsertText','\t');
21976                     this.deferFocus();
21977                     return;
21978                 }
21979                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21980                     this.cleanUpPaste.defer(100, this);
21981                     return;
21982                 }
21983                 
21984              };
21985         }
21986     }(),
21987     
21988     getAllAncestors: function()
21989     {
21990         var p = this.getSelectedNode();
21991         var a = [];
21992         if (!p) {
21993             a.push(p); // push blank onto stack..
21994             p = this.getParentElement();
21995         }
21996         
21997         
21998         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21999             a.push(p);
22000             p = p.parentNode;
22001         }
22002         a.push(this.doc.body);
22003         return a;
22004     },
22005     lastSel : false,
22006     lastSelNode : false,
22007     
22008     
22009     getSelection : function() 
22010     {
22011         this.assignDocWin();
22012         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22013     },
22014     
22015     getSelectedNode: function() 
22016     {
22017         // this may only work on Gecko!!!
22018         
22019         // should we cache this!!!!
22020         
22021         
22022         
22023          
22024         var range = this.createRange(this.getSelection()).cloneRange();
22025         
22026         if (Roo.isIE) {
22027             var parent = range.parentElement();
22028             while (true) {
22029                 var testRange = range.duplicate();
22030                 testRange.moveToElementText(parent);
22031                 if (testRange.inRange(range)) {
22032                     break;
22033                 }
22034                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22035                     break;
22036                 }
22037                 parent = parent.parentElement;
22038             }
22039             return parent;
22040         }
22041         
22042         // is ancestor a text element.
22043         var ac =  range.commonAncestorContainer;
22044         if (ac.nodeType == 3) {
22045             ac = ac.parentNode;
22046         }
22047         
22048         var ar = ac.childNodes;
22049          
22050         var nodes = [];
22051         var other_nodes = [];
22052         var has_other_nodes = false;
22053         for (var i=0;i<ar.length;i++) {
22054             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22055                 continue;
22056             }
22057             // fullly contained node.
22058             
22059             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22060                 nodes.push(ar[i]);
22061                 continue;
22062             }
22063             
22064             // probably selected..
22065             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22066                 other_nodes.push(ar[i]);
22067                 continue;
22068             }
22069             // outer..
22070             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22071                 continue;
22072             }
22073             
22074             
22075             has_other_nodes = true;
22076         }
22077         if (!nodes.length && other_nodes.length) {
22078             nodes= other_nodes;
22079         }
22080         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22081             return false;
22082         }
22083         
22084         return nodes[0];
22085     },
22086     createRange: function(sel)
22087     {
22088         // this has strange effects when using with 
22089         // top toolbar - not sure if it's a great idea.
22090         //this.editor.contentWindow.focus();
22091         if (typeof sel != "undefined") {
22092             try {
22093                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22094             } catch(e) {
22095                 return this.doc.createRange();
22096             }
22097         } else {
22098             return this.doc.createRange();
22099         }
22100     },
22101     getParentElement: function()
22102     {
22103         
22104         this.assignDocWin();
22105         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22106         
22107         var range = this.createRange(sel);
22108          
22109         try {
22110             var p = range.commonAncestorContainer;
22111             while (p.nodeType == 3) { // text node
22112                 p = p.parentNode;
22113             }
22114             return p;
22115         } catch (e) {
22116             return null;
22117         }
22118     
22119     },
22120     /***
22121      *
22122      * Range intersection.. the hard stuff...
22123      *  '-1' = before
22124      *  '0' = hits..
22125      *  '1' = after.
22126      *         [ -- selected range --- ]
22127      *   [fail]                        [fail]
22128      *
22129      *    basically..
22130      *      if end is before start or  hits it. fail.
22131      *      if start is after end or hits it fail.
22132      *
22133      *   if either hits (but other is outside. - then it's not 
22134      *   
22135      *    
22136      **/
22137     
22138     
22139     // @see http://www.thismuchiknow.co.uk/?p=64.
22140     rangeIntersectsNode : function(range, node)
22141     {
22142         var nodeRange = node.ownerDocument.createRange();
22143         try {
22144             nodeRange.selectNode(node);
22145         } catch (e) {
22146             nodeRange.selectNodeContents(node);
22147         }
22148     
22149         var rangeStartRange = range.cloneRange();
22150         rangeStartRange.collapse(true);
22151     
22152         var rangeEndRange = range.cloneRange();
22153         rangeEndRange.collapse(false);
22154     
22155         var nodeStartRange = nodeRange.cloneRange();
22156         nodeStartRange.collapse(true);
22157     
22158         var nodeEndRange = nodeRange.cloneRange();
22159         nodeEndRange.collapse(false);
22160     
22161         return rangeStartRange.compareBoundaryPoints(
22162                  Range.START_TO_START, nodeEndRange) == -1 &&
22163                rangeEndRange.compareBoundaryPoints(
22164                  Range.START_TO_START, nodeStartRange) == 1;
22165         
22166          
22167     },
22168     rangeCompareNode : function(range, node)
22169     {
22170         var nodeRange = node.ownerDocument.createRange();
22171         try {
22172             nodeRange.selectNode(node);
22173         } catch (e) {
22174             nodeRange.selectNodeContents(node);
22175         }
22176         
22177         
22178         range.collapse(true);
22179     
22180         nodeRange.collapse(true);
22181      
22182         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22183         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22184          
22185         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22186         
22187         var nodeIsBefore   =  ss == 1;
22188         var nodeIsAfter    = ee == -1;
22189         
22190         if (nodeIsBefore && nodeIsAfter) {
22191             return 0; // outer
22192         }
22193         if (!nodeIsBefore && nodeIsAfter) {
22194             return 1; //right trailed.
22195         }
22196         
22197         if (nodeIsBefore && !nodeIsAfter) {
22198             return 2;  // left trailed.
22199         }
22200         // fully contined.
22201         return 3;
22202     },
22203
22204     // private? - in a new class?
22205     cleanUpPaste :  function()
22206     {
22207         // cleans up the whole document..
22208         Roo.log('cleanuppaste');
22209         
22210         this.cleanUpChildren(this.doc.body);
22211         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22212         if (clean != this.doc.body.innerHTML) {
22213             this.doc.body.innerHTML = clean;
22214         }
22215         
22216     },
22217     
22218     cleanWordChars : function(input) {// change the chars to hex code
22219         var he = Roo.HtmlEditorCore;
22220         
22221         var output = input;
22222         Roo.each(he.swapCodes, function(sw) { 
22223             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22224             
22225             output = output.replace(swapper, sw[1]);
22226         });
22227         
22228         return output;
22229     },
22230     
22231     
22232     cleanUpChildren : function (n)
22233     {
22234         if (!n.childNodes.length) {
22235             return;
22236         }
22237         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22238            this.cleanUpChild(n.childNodes[i]);
22239         }
22240     },
22241     
22242     
22243         
22244     
22245     cleanUpChild : function (node)
22246     {
22247         var ed = this;
22248         //console.log(node);
22249         if (node.nodeName == "#text") {
22250             // clean up silly Windows -- stuff?
22251             return; 
22252         }
22253         if (node.nodeName == "#comment") {
22254             node.parentNode.removeChild(node);
22255             // clean up silly Windows -- stuff?
22256             return; 
22257         }
22258         var lcname = node.tagName.toLowerCase();
22259         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22260         // whitelist of tags..
22261         
22262         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22263             // remove node.
22264             node.parentNode.removeChild(node);
22265             return;
22266             
22267         }
22268         
22269         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22270         
22271         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22272         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22273         
22274         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22275         //    remove_keep_children = true;
22276         //}
22277         
22278         if (remove_keep_children) {
22279             this.cleanUpChildren(node);
22280             // inserts everything just before this node...
22281             while (node.childNodes.length) {
22282                 var cn = node.childNodes[0];
22283                 node.removeChild(cn);
22284                 node.parentNode.insertBefore(cn, node);
22285             }
22286             node.parentNode.removeChild(node);
22287             return;
22288         }
22289         
22290         if (!node.attributes || !node.attributes.length) {
22291             this.cleanUpChildren(node);
22292             return;
22293         }
22294         
22295         function cleanAttr(n,v)
22296         {
22297             
22298             if (v.match(/^\./) || v.match(/^\//)) {
22299                 return;
22300             }
22301             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22302                 return;
22303             }
22304             if (v.match(/^#/)) {
22305                 return;
22306             }
22307 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22308             node.removeAttribute(n);
22309             
22310         }
22311         
22312         var cwhite = this.cwhite;
22313         var cblack = this.cblack;
22314             
22315         function cleanStyle(n,v)
22316         {
22317             if (v.match(/expression/)) { //XSS?? should we even bother..
22318                 node.removeAttribute(n);
22319                 return;
22320             }
22321             
22322             var parts = v.split(/;/);
22323             var clean = [];
22324             
22325             Roo.each(parts, function(p) {
22326                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22327                 if (!p.length) {
22328                     return true;
22329                 }
22330                 var l = p.split(':').shift().replace(/\s+/g,'');
22331                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22332                 
22333                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22334 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22335                     //node.removeAttribute(n);
22336                     return true;
22337                 }
22338                 //Roo.log()
22339                 // only allow 'c whitelisted system attributes'
22340                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22341 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22342                     //node.removeAttribute(n);
22343                     return true;
22344                 }
22345                 
22346                 
22347                  
22348                 
22349                 clean.push(p);
22350                 return true;
22351             });
22352             if (clean.length) { 
22353                 node.setAttribute(n, clean.join(';'));
22354             } else {
22355                 node.removeAttribute(n);
22356             }
22357             
22358         }
22359         
22360         
22361         for (var i = node.attributes.length-1; i > -1 ; i--) {
22362             var a = node.attributes[i];
22363             //console.log(a);
22364             
22365             if (a.name.toLowerCase().substr(0,2)=='on')  {
22366                 node.removeAttribute(a.name);
22367                 continue;
22368             }
22369             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22370                 node.removeAttribute(a.name);
22371                 continue;
22372             }
22373             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22374                 cleanAttr(a.name,a.value); // fixme..
22375                 continue;
22376             }
22377             if (a.name == 'style') {
22378                 cleanStyle(a.name,a.value);
22379                 continue;
22380             }
22381             /// clean up MS crap..
22382             // tecnically this should be a list of valid class'es..
22383             
22384             
22385             if (a.name == 'class') {
22386                 if (a.value.match(/^Mso/)) {
22387                     node.className = '';
22388                 }
22389                 
22390                 if (a.value.match(/^body$/)) {
22391                     node.className = '';
22392                 }
22393                 continue;
22394             }
22395             
22396             // style cleanup!?
22397             // class cleanup?
22398             
22399         }
22400         
22401         
22402         this.cleanUpChildren(node);
22403         
22404         
22405     },
22406     
22407     /**
22408      * Clean up MS wordisms...
22409      */
22410     cleanWord : function(node)
22411     {
22412         
22413         
22414         if (!node) {
22415             this.cleanWord(this.doc.body);
22416             return;
22417         }
22418         if (node.nodeName == "#text") {
22419             // clean up silly Windows -- stuff?
22420             return; 
22421         }
22422         if (node.nodeName == "#comment") {
22423             node.parentNode.removeChild(node);
22424             // clean up silly Windows -- stuff?
22425             return; 
22426         }
22427         
22428         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22429             node.parentNode.removeChild(node);
22430             return;
22431         }
22432         
22433         // remove - but keep children..
22434         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22435             while (node.childNodes.length) {
22436                 var cn = node.childNodes[0];
22437                 node.removeChild(cn);
22438                 node.parentNode.insertBefore(cn, node);
22439             }
22440             node.parentNode.removeChild(node);
22441             this.iterateChildren(node, this.cleanWord);
22442             return;
22443         }
22444         // clean styles
22445         if (node.className.length) {
22446             
22447             var cn = node.className.split(/\W+/);
22448             var cna = [];
22449             Roo.each(cn, function(cls) {
22450                 if (cls.match(/Mso[a-zA-Z]+/)) {
22451                     return;
22452                 }
22453                 cna.push(cls);
22454             });
22455             node.className = cna.length ? cna.join(' ') : '';
22456             if (!cna.length) {
22457                 node.removeAttribute("class");
22458             }
22459         }
22460         
22461         if (node.hasAttribute("lang")) {
22462             node.removeAttribute("lang");
22463         }
22464         
22465         if (node.hasAttribute("style")) {
22466             
22467             var styles = node.getAttribute("style").split(";");
22468             var nstyle = [];
22469             Roo.each(styles, function(s) {
22470                 if (!s.match(/:/)) {
22471                     return;
22472                 }
22473                 var kv = s.split(":");
22474                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22475                     return;
22476                 }
22477                 // what ever is left... we allow.
22478                 nstyle.push(s);
22479             });
22480             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22481             if (!nstyle.length) {
22482                 node.removeAttribute('style');
22483             }
22484         }
22485         this.iterateChildren(node, this.cleanWord);
22486         
22487         
22488         
22489     },
22490     /**
22491      * iterateChildren of a Node, calling fn each time, using this as the scole..
22492      * @param {DomNode} node node to iterate children of.
22493      * @param {Function} fn method of this class to call on each item.
22494      */
22495     iterateChildren : function(node, fn)
22496     {
22497         if (!node.childNodes.length) {
22498                 return;
22499         }
22500         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22501            fn.call(this, node.childNodes[i])
22502         }
22503     },
22504     
22505     
22506     /**
22507      * cleanTableWidths.
22508      *
22509      * Quite often pasting from word etc.. results in tables with column and widths.
22510      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22511      *
22512      */
22513     cleanTableWidths : function(node)
22514     {
22515          
22516          
22517         if (!node) {
22518             this.cleanTableWidths(this.doc.body);
22519             return;
22520         }
22521         
22522         // ignore list...
22523         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22524             return; 
22525         }
22526         Roo.log(node.tagName);
22527         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22528             this.iterateChildren(node, this.cleanTableWidths);
22529             return;
22530         }
22531         if (node.hasAttribute('width')) {
22532             node.removeAttribute('width');
22533         }
22534         
22535          
22536         if (node.hasAttribute("style")) {
22537             // pretty basic...
22538             
22539             var styles = node.getAttribute("style").split(";");
22540             var nstyle = [];
22541             Roo.each(styles, function(s) {
22542                 if (!s.match(/:/)) {
22543                     return;
22544                 }
22545                 var kv = s.split(":");
22546                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22547                     return;
22548                 }
22549                 // what ever is left... we allow.
22550                 nstyle.push(s);
22551             });
22552             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22553             if (!nstyle.length) {
22554                 node.removeAttribute('style');
22555             }
22556         }
22557         
22558         this.iterateChildren(node, this.cleanTableWidths);
22559         
22560         
22561     },
22562     
22563     
22564     
22565     
22566     domToHTML : function(currentElement, depth, nopadtext) {
22567         
22568         depth = depth || 0;
22569         nopadtext = nopadtext || false;
22570     
22571         if (!currentElement) {
22572             return this.domToHTML(this.doc.body);
22573         }
22574         
22575         //Roo.log(currentElement);
22576         var j;
22577         var allText = false;
22578         var nodeName = currentElement.nodeName;
22579         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22580         
22581         if  (nodeName == '#text') {
22582             
22583             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22584         }
22585         
22586         
22587         var ret = '';
22588         if (nodeName != 'BODY') {
22589              
22590             var i = 0;
22591             // Prints the node tagName, such as <A>, <IMG>, etc
22592             if (tagName) {
22593                 var attr = [];
22594                 for(i = 0; i < currentElement.attributes.length;i++) {
22595                     // quoting?
22596                     var aname = currentElement.attributes.item(i).name;
22597                     if (!currentElement.attributes.item(i).value.length) {
22598                         continue;
22599                     }
22600                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22601                 }
22602                 
22603                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22604             } 
22605             else {
22606                 
22607                 // eack
22608             }
22609         } else {
22610             tagName = false;
22611         }
22612         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22613             return ret;
22614         }
22615         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22616             nopadtext = true;
22617         }
22618         
22619         
22620         // Traverse the tree
22621         i = 0;
22622         var currentElementChild = currentElement.childNodes.item(i);
22623         var allText = true;
22624         var innerHTML  = '';
22625         lastnode = '';
22626         while (currentElementChild) {
22627             // Formatting code (indent the tree so it looks nice on the screen)
22628             var nopad = nopadtext;
22629             if (lastnode == 'SPAN') {
22630                 nopad  = true;
22631             }
22632             // text
22633             if  (currentElementChild.nodeName == '#text') {
22634                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22635                 toadd = nopadtext ? toadd : toadd.trim();
22636                 if (!nopad && toadd.length > 80) {
22637                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22638                 }
22639                 innerHTML  += toadd;
22640                 
22641                 i++;
22642                 currentElementChild = currentElement.childNodes.item(i);
22643                 lastNode = '';
22644                 continue;
22645             }
22646             allText = false;
22647             
22648             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22649                 
22650             // Recursively traverse the tree structure of the child node
22651             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22652             lastnode = currentElementChild.nodeName;
22653             i++;
22654             currentElementChild=currentElement.childNodes.item(i);
22655         }
22656         
22657         ret += innerHTML;
22658         
22659         if (!allText) {
22660                 // The remaining code is mostly for formatting the tree
22661             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22662         }
22663         
22664         
22665         if (tagName) {
22666             ret+= "</"+tagName+">";
22667         }
22668         return ret;
22669         
22670     },
22671         
22672     applyBlacklists : function()
22673     {
22674         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22675         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22676         
22677         this.white = [];
22678         this.black = [];
22679         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22680             if (b.indexOf(tag) > -1) {
22681                 return;
22682             }
22683             this.white.push(tag);
22684             
22685         }, this);
22686         
22687         Roo.each(w, function(tag) {
22688             if (b.indexOf(tag) > -1) {
22689                 return;
22690             }
22691             if (this.white.indexOf(tag) > -1) {
22692                 return;
22693             }
22694             this.white.push(tag);
22695             
22696         }, this);
22697         
22698         
22699         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22700             if (w.indexOf(tag) > -1) {
22701                 return;
22702             }
22703             this.black.push(tag);
22704             
22705         }, this);
22706         
22707         Roo.each(b, function(tag) {
22708             if (w.indexOf(tag) > -1) {
22709                 return;
22710             }
22711             if (this.black.indexOf(tag) > -1) {
22712                 return;
22713             }
22714             this.black.push(tag);
22715             
22716         }, this);
22717         
22718         
22719         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22720         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22721         
22722         this.cwhite = [];
22723         this.cblack = [];
22724         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22725             if (b.indexOf(tag) > -1) {
22726                 return;
22727             }
22728             this.cwhite.push(tag);
22729             
22730         }, this);
22731         
22732         Roo.each(w, function(tag) {
22733             if (b.indexOf(tag) > -1) {
22734                 return;
22735             }
22736             if (this.cwhite.indexOf(tag) > -1) {
22737                 return;
22738             }
22739             this.cwhite.push(tag);
22740             
22741         }, this);
22742         
22743         
22744         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22745             if (w.indexOf(tag) > -1) {
22746                 return;
22747             }
22748             this.cblack.push(tag);
22749             
22750         }, this);
22751         
22752         Roo.each(b, function(tag) {
22753             if (w.indexOf(tag) > -1) {
22754                 return;
22755             }
22756             if (this.cblack.indexOf(tag) > -1) {
22757                 return;
22758             }
22759             this.cblack.push(tag);
22760             
22761         }, this);
22762     },
22763     
22764     setStylesheets : function(stylesheets)
22765     {
22766         if(typeof(stylesheets) == 'string'){
22767             Roo.get(this.iframe.contentDocument.head).createChild({
22768                 tag : 'link',
22769                 rel : 'stylesheet',
22770                 type : 'text/css',
22771                 href : stylesheets
22772             });
22773             
22774             return;
22775         }
22776         var _this = this;
22777      
22778         Roo.each(stylesheets, function(s) {
22779             if(!s.length){
22780                 return;
22781             }
22782             
22783             Roo.get(_this.iframe.contentDocument.head).createChild({
22784                 tag : 'link',
22785                 rel : 'stylesheet',
22786                 type : 'text/css',
22787                 href : s
22788             });
22789         });
22790
22791         
22792     },
22793     
22794     removeStylesheets : function()
22795     {
22796         var _this = this;
22797         
22798         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22799             s.remove();
22800         });
22801     },
22802     
22803     setStyle : function(style)
22804     {
22805         Roo.get(this.iframe.contentDocument.head).createChild({
22806             tag : 'style',
22807             type : 'text/css',
22808             html : style
22809         });
22810
22811         return;
22812     }
22813     
22814     // hide stuff that is not compatible
22815     /**
22816      * @event blur
22817      * @hide
22818      */
22819     /**
22820      * @event change
22821      * @hide
22822      */
22823     /**
22824      * @event focus
22825      * @hide
22826      */
22827     /**
22828      * @event specialkey
22829      * @hide
22830      */
22831     /**
22832      * @cfg {String} fieldClass @hide
22833      */
22834     /**
22835      * @cfg {String} focusClass @hide
22836      */
22837     /**
22838      * @cfg {String} autoCreate @hide
22839      */
22840     /**
22841      * @cfg {String} inputType @hide
22842      */
22843     /**
22844      * @cfg {String} invalidClass @hide
22845      */
22846     /**
22847      * @cfg {String} invalidText @hide
22848      */
22849     /**
22850      * @cfg {String} msgFx @hide
22851      */
22852     /**
22853      * @cfg {String} validateOnBlur @hide
22854      */
22855 });
22856
22857 Roo.HtmlEditorCore.white = [
22858         'area', 'br', 'img', 'input', 'hr', 'wbr',
22859         
22860        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22861        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22862        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22863        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22864        'table',   'ul',         'xmp', 
22865        
22866        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22867       'thead',   'tr', 
22868      
22869       'dir', 'menu', 'ol', 'ul', 'dl',
22870        
22871       'embed',  'object'
22872 ];
22873
22874
22875 Roo.HtmlEditorCore.black = [
22876     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22877         'applet', // 
22878         'base',   'basefont', 'bgsound', 'blink',  'body', 
22879         'frame',  'frameset', 'head',    'html',   'ilayer', 
22880         'iframe', 'layer',  'link',     'meta',    'object',   
22881         'script', 'style' ,'title',  'xml' // clean later..
22882 ];
22883 Roo.HtmlEditorCore.clean = [
22884     'script', 'style', 'title', 'xml'
22885 ];
22886 Roo.HtmlEditorCore.remove = [
22887     'font'
22888 ];
22889 // attributes..
22890
22891 Roo.HtmlEditorCore.ablack = [
22892     'on'
22893 ];
22894     
22895 Roo.HtmlEditorCore.aclean = [ 
22896     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22897 ];
22898
22899 // protocols..
22900 Roo.HtmlEditorCore.pwhite= [
22901         'http',  'https',  'mailto'
22902 ];
22903
22904 // white listed style attributes.
22905 Roo.HtmlEditorCore.cwhite= [
22906       //  'text-align', /// default is to allow most things..
22907       
22908          
22909 //        'font-size'//??
22910 ];
22911
22912 // black listed style attributes.
22913 Roo.HtmlEditorCore.cblack= [
22914       //  'font-size' -- this can be set by the project 
22915 ];
22916
22917
22918 Roo.HtmlEditorCore.swapCodes   =[ 
22919     [    8211, "--" ], 
22920     [    8212, "--" ], 
22921     [    8216,  "'" ],  
22922     [    8217, "'" ],  
22923     [    8220, '"' ],  
22924     [    8221, '"' ],  
22925     [    8226, "*" ],  
22926     [    8230, "..." ]
22927 ]; 
22928
22929     /*
22930  * - LGPL
22931  *
22932  * HtmlEditor
22933  * 
22934  */
22935
22936 /**
22937  * @class Roo.bootstrap.HtmlEditor
22938  * @extends Roo.bootstrap.TextArea
22939  * Bootstrap HtmlEditor class
22940
22941  * @constructor
22942  * Create a new HtmlEditor
22943  * @param {Object} config The config object
22944  */
22945
22946 Roo.bootstrap.HtmlEditor = function(config){
22947     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22948     if (!this.toolbars) {
22949         this.toolbars = [];
22950     }
22951     
22952     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22953     this.addEvents({
22954             /**
22955              * @event initialize
22956              * Fires when the editor is fully initialized (including the iframe)
22957              * @param {HtmlEditor} this
22958              */
22959             initialize: true,
22960             /**
22961              * @event activate
22962              * Fires when the editor is first receives the focus. Any insertion must wait
22963              * until after this event.
22964              * @param {HtmlEditor} this
22965              */
22966             activate: true,
22967              /**
22968              * @event beforesync
22969              * Fires before the textarea is updated with content from the editor iframe. Return false
22970              * to cancel the sync.
22971              * @param {HtmlEditor} this
22972              * @param {String} html
22973              */
22974             beforesync: true,
22975              /**
22976              * @event beforepush
22977              * Fires before the iframe editor is updated with content from the textarea. Return false
22978              * to cancel the push.
22979              * @param {HtmlEditor} this
22980              * @param {String} html
22981              */
22982             beforepush: true,
22983              /**
22984              * @event sync
22985              * Fires when the textarea is updated with content from the editor iframe.
22986              * @param {HtmlEditor} this
22987              * @param {String} html
22988              */
22989             sync: true,
22990              /**
22991              * @event push
22992              * Fires when the iframe editor is updated with content from the textarea.
22993              * @param {HtmlEditor} this
22994              * @param {String} html
22995              */
22996             push: true,
22997              /**
22998              * @event editmodechange
22999              * Fires when the editor switches edit modes
23000              * @param {HtmlEditor} this
23001              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23002              */
23003             editmodechange: true,
23004             /**
23005              * @event editorevent
23006              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23007              * @param {HtmlEditor} this
23008              */
23009             editorevent: true,
23010             /**
23011              * @event firstfocus
23012              * Fires when on first focus - needed by toolbars..
23013              * @param {HtmlEditor} this
23014              */
23015             firstfocus: true,
23016             /**
23017              * @event autosave
23018              * Auto save the htmlEditor value as a file into Events
23019              * @param {HtmlEditor} this
23020              */
23021             autosave: true,
23022             /**
23023              * @event savedpreview
23024              * preview the saved version of htmlEditor
23025              * @param {HtmlEditor} this
23026              */
23027             savedpreview: true
23028         });
23029 };
23030
23031
23032 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23033     
23034     
23035       /**
23036      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23037      */
23038     toolbars : false,
23039     
23040      /**
23041     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23042     */
23043     btns : [],
23044    
23045      /**
23046      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23047      *                        Roo.resizable.
23048      */
23049     resizable : false,
23050      /**
23051      * @cfg {Number} height (in pixels)
23052      */   
23053     height: 300,
23054    /**
23055      * @cfg {Number} width (in pixels)
23056      */   
23057     width: false,
23058     
23059     /**
23060      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23061      * 
23062      */
23063     stylesheets: false,
23064     
23065     // id of frame..
23066     frameId: false,
23067     
23068     // private properties
23069     validationEvent : false,
23070     deferHeight: true,
23071     initialized : false,
23072     activated : false,
23073     
23074     onFocus : Roo.emptyFn,
23075     iframePad:3,
23076     hideMode:'offsets',
23077     
23078     tbContainer : false,
23079     
23080     bodyCls : '',
23081     
23082     toolbarContainer :function() {
23083         return this.wrap.select('.x-html-editor-tb',true).first();
23084     },
23085
23086     /**
23087      * Protected method that will not generally be called directly. It
23088      * is called when the editor creates its toolbar. Override this method if you need to
23089      * add custom toolbar buttons.
23090      * @param {HtmlEditor} editor
23091      */
23092     createToolbar : function(){
23093         Roo.log('renewing');
23094         Roo.log("create toolbars");
23095         
23096         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23097         this.toolbars[0].render(this.toolbarContainer());
23098         
23099         return;
23100         
23101 //        if (!editor.toolbars || !editor.toolbars.length) {
23102 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23103 //        }
23104 //        
23105 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23106 //            editor.toolbars[i] = Roo.factory(
23107 //                    typeof(editor.toolbars[i]) == 'string' ?
23108 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23109 //                Roo.bootstrap.HtmlEditor);
23110 //            editor.toolbars[i].init(editor);
23111 //        }
23112     },
23113
23114      
23115     // private
23116     onRender : function(ct, position)
23117     {
23118        // Roo.log("Call onRender: " + this.xtype);
23119         var _t = this;
23120         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23121       
23122         this.wrap = this.inputEl().wrap({
23123             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23124         });
23125         
23126         this.editorcore.onRender(ct, position);
23127          
23128         if (this.resizable) {
23129             this.resizeEl = new Roo.Resizable(this.wrap, {
23130                 pinned : true,
23131                 wrap: true,
23132                 dynamic : true,
23133                 minHeight : this.height,
23134                 height: this.height,
23135                 handles : this.resizable,
23136                 width: this.width,
23137                 listeners : {
23138                     resize : function(r, w, h) {
23139                         _t.onResize(w,h); // -something
23140                     }
23141                 }
23142             });
23143             
23144         }
23145         this.createToolbar(this);
23146        
23147         
23148         if(!this.width && this.resizable){
23149             this.setSize(this.wrap.getSize());
23150         }
23151         if (this.resizeEl) {
23152             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23153             // should trigger onReize..
23154         }
23155         
23156     },
23157
23158     // private
23159     onResize : function(w, h)
23160     {
23161         Roo.log('resize: ' +w + ',' + h );
23162         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23163         var ew = false;
23164         var eh = false;
23165         
23166         if(this.inputEl() ){
23167             if(typeof w == 'number'){
23168                 var aw = w - this.wrap.getFrameWidth('lr');
23169                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23170                 ew = aw;
23171             }
23172             if(typeof h == 'number'){
23173                  var tbh = -11;  // fixme it needs to tool bar size!
23174                 for (var i =0; i < this.toolbars.length;i++) {
23175                     // fixme - ask toolbars for heights?
23176                     tbh += this.toolbars[i].el.getHeight();
23177                     //if (this.toolbars[i].footer) {
23178                     //    tbh += this.toolbars[i].footer.el.getHeight();
23179                     //}
23180                 }
23181               
23182                 
23183                 
23184                 
23185                 
23186                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23187                 ah -= 5; // knock a few pixes off for look..
23188                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23189                 var eh = ah;
23190             }
23191         }
23192         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23193         this.editorcore.onResize(ew,eh);
23194         
23195     },
23196
23197     /**
23198      * Toggles the editor between standard and source edit mode.
23199      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23200      */
23201     toggleSourceEdit : function(sourceEditMode)
23202     {
23203         this.editorcore.toggleSourceEdit(sourceEditMode);
23204         
23205         if(this.editorcore.sourceEditMode){
23206             Roo.log('editor - showing textarea');
23207             
23208 //            Roo.log('in');
23209 //            Roo.log(this.syncValue());
23210             this.syncValue();
23211             this.inputEl().removeClass(['hide', 'x-hidden']);
23212             this.inputEl().dom.removeAttribute('tabIndex');
23213             this.inputEl().focus();
23214         }else{
23215             Roo.log('editor - hiding textarea');
23216 //            Roo.log('out')
23217 //            Roo.log(this.pushValue()); 
23218             this.pushValue();
23219             
23220             this.inputEl().addClass(['hide', 'x-hidden']);
23221             this.inputEl().dom.setAttribute('tabIndex', -1);
23222             //this.deferFocus();
23223         }
23224          
23225         if(this.resizable){
23226             this.setSize(this.wrap.getSize());
23227         }
23228         
23229         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23230     },
23231  
23232     // private (for BoxComponent)
23233     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23234
23235     // private (for BoxComponent)
23236     getResizeEl : function(){
23237         return this.wrap;
23238     },
23239
23240     // private (for BoxComponent)
23241     getPositionEl : function(){
23242         return this.wrap;
23243     },
23244
23245     // private
23246     initEvents : function(){
23247         this.originalValue = this.getValue();
23248     },
23249
23250 //    /**
23251 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23252 //     * @method
23253 //     */
23254 //    markInvalid : Roo.emptyFn,
23255 //    /**
23256 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23257 //     * @method
23258 //     */
23259 //    clearInvalid : Roo.emptyFn,
23260
23261     setValue : function(v){
23262         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23263         this.editorcore.pushValue();
23264     },
23265
23266      
23267     // private
23268     deferFocus : function(){
23269         this.focus.defer(10, this);
23270     },
23271
23272     // doc'ed in Field
23273     focus : function(){
23274         this.editorcore.focus();
23275         
23276     },
23277       
23278
23279     // private
23280     onDestroy : function(){
23281         
23282         
23283         
23284         if(this.rendered){
23285             
23286             for (var i =0; i < this.toolbars.length;i++) {
23287                 // fixme - ask toolbars for heights?
23288                 this.toolbars[i].onDestroy();
23289             }
23290             
23291             this.wrap.dom.innerHTML = '';
23292             this.wrap.remove();
23293         }
23294     },
23295
23296     // private
23297     onFirstFocus : function(){
23298         //Roo.log("onFirstFocus");
23299         this.editorcore.onFirstFocus();
23300          for (var i =0; i < this.toolbars.length;i++) {
23301             this.toolbars[i].onFirstFocus();
23302         }
23303         
23304     },
23305     
23306     // private
23307     syncValue : function()
23308     {   
23309         this.editorcore.syncValue();
23310     },
23311     
23312     pushValue : function()
23313     {   
23314         this.editorcore.pushValue();
23315     }
23316      
23317     
23318     // hide stuff that is not compatible
23319     /**
23320      * @event blur
23321      * @hide
23322      */
23323     /**
23324      * @event change
23325      * @hide
23326      */
23327     /**
23328      * @event focus
23329      * @hide
23330      */
23331     /**
23332      * @event specialkey
23333      * @hide
23334      */
23335     /**
23336      * @cfg {String} fieldClass @hide
23337      */
23338     /**
23339      * @cfg {String} focusClass @hide
23340      */
23341     /**
23342      * @cfg {String} autoCreate @hide
23343      */
23344     /**
23345      * @cfg {String} inputType @hide
23346      */
23347     /**
23348      * @cfg {String} invalidClass @hide
23349      */
23350     /**
23351      * @cfg {String} invalidText @hide
23352      */
23353     /**
23354      * @cfg {String} msgFx @hide
23355      */
23356     /**
23357      * @cfg {String} validateOnBlur @hide
23358      */
23359 });
23360  
23361     
23362    
23363    
23364    
23365       
23366 Roo.namespace('Roo.bootstrap.htmleditor');
23367 /**
23368  * @class Roo.bootstrap.HtmlEditorToolbar1
23369  * Basic Toolbar
23370  * 
23371  * Usage:
23372  *
23373  new Roo.bootstrap.HtmlEditor({
23374     ....
23375     toolbars : [
23376         new Roo.bootstrap.HtmlEditorToolbar1({
23377             disable : { fonts: 1 , format: 1, ..., ... , ...],
23378             btns : [ .... ]
23379         })
23380     }
23381      
23382  * 
23383  * @cfg {Object} disable List of elements to disable..
23384  * @cfg {Array} btns List of additional buttons.
23385  * 
23386  * 
23387  * NEEDS Extra CSS? 
23388  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23389  */
23390  
23391 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23392 {
23393     
23394     Roo.apply(this, config);
23395     
23396     // default disabled, based on 'good practice'..
23397     this.disable = this.disable || {};
23398     Roo.applyIf(this.disable, {
23399         fontSize : true,
23400         colors : true,
23401         specialElements : true
23402     });
23403     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23404     
23405     this.editor = config.editor;
23406     this.editorcore = config.editor.editorcore;
23407     
23408     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23409     
23410     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23411     // dont call parent... till later.
23412 }
23413 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23414      
23415     bar : true,
23416     
23417     editor : false,
23418     editorcore : false,
23419     
23420     
23421     formats : [
23422         "p" ,  
23423         "h1","h2","h3","h4","h5","h6", 
23424         "pre", "code", 
23425         "abbr", "acronym", "address", "cite", "samp", "var",
23426         'div','span'
23427     ],
23428     
23429     onRender : function(ct, position)
23430     {
23431        // Roo.log("Call onRender: " + this.xtype);
23432         
23433        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23434        Roo.log(this.el);
23435        this.el.dom.style.marginBottom = '0';
23436        var _this = this;
23437        var editorcore = this.editorcore;
23438        var editor= this.editor;
23439        
23440        var children = [];
23441        var btn = function(id,cmd , toggle, handler, html){
23442        
23443             var  event = toggle ? 'toggle' : 'click';
23444        
23445             var a = {
23446                 size : 'sm',
23447                 xtype: 'Button',
23448                 xns: Roo.bootstrap,
23449                 glyphicon : id,
23450                 cmd : id || cmd,
23451                 enableToggle:toggle !== false,
23452                 html : html || '',
23453                 pressed : toggle ? false : null,
23454                 listeners : {}
23455             };
23456             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23457                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23458             };
23459             children.push(a);
23460             return a;
23461        }
23462        
23463     //    var cb_box = function...
23464         
23465         var style = {
23466                 xtype: 'Button',
23467                 size : 'sm',
23468                 xns: Roo.bootstrap,
23469                 glyphicon : 'font',
23470                 //html : 'submit'
23471                 menu : {
23472                     xtype: 'Menu',
23473                     xns: Roo.bootstrap,
23474                     items:  []
23475                 }
23476         };
23477         Roo.each(this.formats, function(f) {
23478             style.menu.items.push({
23479                 xtype :'MenuItem',
23480                 xns: Roo.bootstrap,
23481                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23482                 tagname : f,
23483                 listeners : {
23484                     click : function()
23485                     {
23486                         editorcore.insertTag(this.tagname);
23487                         editor.focus();
23488                     }
23489                 }
23490                 
23491             });
23492         });
23493         children.push(style);   
23494         
23495         btn('bold',false,true);
23496         btn('italic',false,true);
23497         btn('align-left', 'justifyleft',true);
23498         btn('align-center', 'justifycenter',true);
23499         btn('align-right' , 'justifyright',true);
23500         btn('link', false, false, function(btn) {
23501             //Roo.log("create link?");
23502             var url = prompt(this.createLinkText, this.defaultLinkValue);
23503             if(url && url != 'http:/'+'/'){
23504                 this.editorcore.relayCmd('createlink', url);
23505             }
23506         }),
23507         btn('list','insertunorderedlist',true);
23508         btn('pencil', false,true, function(btn){
23509                 Roo.log(this);
23510                 this.toggleSourceEdit(btn.pressed);
23511         });
23512         
23513         if (this.editor.btns.length > 0) {
23514             for (var i = 0; i<this.editor.btns.length; i++) {
23515                 children.push(this.editor.btns[i]);
23516             }
23517         }
23518         
23519         /*
23520         var cog = {
23521                 xtype: 'Button',
23522                 size : 'sm',
23523                 xns: Roo.bootstrap,
23524                 glyphicon : 'cog',
23525                 //html : 'submit'
23526                 menu : {
23527                     xtype: 'Menu',
23528                     xns: Roo.bootstrap,
23529                     items:  []
23530                 }
23531         };
23532         
23533         cog.menu.items.push({
23534             xtype :'MenuItem',
23535             xns: Roo.bootstrap,
23536             html : Clean styles,
23537             tagname : f,
23538             listeners : {
23539                 click : function()
23540                 {
23541                     editorcore.insertTag(this.tagname);
23542                     editor.focus();
23543                 }
23544             }
23545             
23546         });
23547        */
23548         
23549          
23550        this.xtype = 'NavSimplebar';
23551         
23552         for(var i=0;i< children.length;i++) {
23553             
23554             this.buttons.add(this.addxtypeChild(children[i]));
23555             
23556         }
23557         
23558         editor.on('editorevent', this.updateToolbar, this);
23559     },
23560     onBtnClick : function(id)
23561     {
23562        this.editorcore.relayCmd(id);
23563        this.editorcore.focus();
23564     },
23565     
23566     /**
23567      * Protected method that will not generally be called directly. It triggers
23568      * a toolbar update by reading the markup state of the current selection in the editor.
23569      */
23570     updateToolbar: function(){
23571
23572         if(!this.editorcore.activated){
23573             this.editor.onFirstFocus(); // is this neeed?
23574             return;
23575         }
23576
23577         var btns = this.buttons; 
23578         var doc = this.editorcore.doc;
23579         btns.get('bold').setActive(doc.queryCommandState('bold'));
23580         btns.get('italic').setActive(doc.queryCommandState('italic'));
23581         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23582         
23583         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23584         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23585         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23586         
23587         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23588         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23589          /*
23590         
23591         var ans = this.editorcore.getAllAncestors();
23592         if (this.formatCombo) {
23593             
23594             
23595             var store = this.formatCombo.store;
23596             this.formatCombo.setValue("");
23597             for (var i =0; i < ans.length;i++) {
23598                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23599                     // select it..
23600                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23601                     break;
23602                 }
23603             }
23604         }
23605         
23606         
23607         
23608         // hides menus... - so this cant be on a menu...
23609         Roo.bootstrap.MenuMgr.hideAll();
23610         */
23611         Roo.bootstrap.MenuMgr.hideAll();
23612         //this.editorsyncValue();
23613     },
23614     onFirstFocus: function() {
23615         this.buttons.each(function(item){
23616            item.enable();
23617         });
23618     },
23619     toggleSourceEdit : function(sourceEditMode){
23620         
23621           
23622         if(sourceEditMode){
23623             Roo.log("disabling buttons");
23624            this.buttons.each( function(item){
23625                 if(item.cmd != 'pencil'){
23626                     item.disable();
23627                 }
23628             });
23629           
23630         }else{
23631             Roo.log("enabling buttons");
23632             if(this.editorcore.initialized){
23633                 this.buttons.each( function(item){
23634                     item.enable();
23635                 });
23636             }
23637             
23638         }
23639         Roo.log("calling toggole on editor");
23640         // tell the editor that it's been pressed..
23641         this.editor.toggleSourceEdit(sourceEditMode);
23642        
23643     }
23644 });
23645
23646
23647
23648
23649
23650 /**
23651  * @class Roo.bootstrap.Table.AbstractSelectionModel
23652  * @extends Roo.util.Observable
23653  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23654  * implemented by descendant classes.  This class should not be directly instantiated.
23655  * @constructor
23656  */
23657 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23658     this.locked = false;
23659     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23660 };
23661
23662
23663 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23664     /** @ignore Called by the grid automatically. Do not call directly. */
23665     init : function(grid){
23666         this.grid = grid;
23667         this.initEvents();
23668     },
23669
23670     /**
23671      * Locks the selections.
23672      */
23673     lock : function(){
23674         this.locked = true;
23675     },
23676
23677     /**
23678      * Unlocks the selections.
23679      */
23680     unlock : function(){
23681         this.locked = false;
23682     },
23683
23684     /**
23685      * Returns true if the selections are locked.
23686      * @return {Boolean}
23687      */
23688     isLocked : function(){
23689         return this.locked;
23690     }
23691 });
23692 /**
23693  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23694  * @class Roo.bootstrap.Table.RowSelectionModel
23695  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23696  * It supports multiple selections and keyboard selection/navigation. 
23697  * @constructor
23698  * @param {Object} config
23699  */
23700
23701 Roo.bootstrap.Table.RowSelectionModel = function(config){
23702     Roo.apply(this, config);
23703     this.selections = new Roo.util.MixedCollection(false, function(o){
23704         return o.id;
23705     });
23706
23707     this.last = false;
23708     this.lastActive = false;
23709
23710     this.addEvents({
23711         /**
23712              * @event selectionchange
23713              * Fires when the selection changes
23714              * @param {SelectionModel} this
23715              */
23716             "selectionchange" : true,
23717         /**
23718              * @event afterselectionchange
23719              * Fires after the selection changes (eg. by key press or clicking)
23720              * @param {SelectionModel} this
23721              */
23722             "afterselectionchange" : true,
23723         /**
23724              * @event beforerowselect
23725              * Fires when a row is selected being selected, return false to cancel.
23726              * @param {SelectionModel} this
23727              * @param {Number} rowIndex The selected index
23728              * @param {Boolean} keepExisting False if other selections will be cleared
23729              */
23730             "beforerowselect" : true,
23731         /**
23732              * @event rowselect
23733              * Fires when a row is selected.
23734              * @param {SelectionModel} this
23735              * @param {Number} rowIndex The selected index
23736              * @param {Roo.data.Record} r The record
23737              */
23738             "rowselect" : true,
23739         /**
23740              * @event rowdeselect
23741              * Fires when a row is deselected.
23742              * @param {SelectionModel} this
23743              * @param {Number} rowIndex The selected index
23744              */
23745         "rowdeselect" : true
23746     });
23747     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23748     this.locked = false;
23749  };
23750
23751 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23752     /**
23753      * @cfg {Boolean} singleSelect
23754      * True to allow selection of only one row at a time (defaults to false)
23755      */
23756     singleSelect : false,
23757
23758     // private
23759     initEvents : function()
23760     {
23761
23762         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23763         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23764         //}else{ // allow click to work like normal
23765          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23766         //}
23767         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23768         this.grid.on("rowclick", this.handleMouseDown, this);
23769         
23770         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23771             "up" : function(e){
23772                 if(!e.shiftKey){
23773                     this.selectPrevious(e.shiftKey);
23774                 }else if(this.last !== false && this.lastActive !== false){
23775                     var last = this.last;
23776                     this.selectRange(this.last,  this.lastActive-1);
23777                     this.grid.getView().focusRow(this.lastActive);
23778                     if(last !== false){
23779                         this.last = last;
23780                     }
23781                 }else{
23782                     this.selectFirstRow();
23783                 }
23784                 this.fireEvent("afterselectionchange", this);
23785             },
23786             "down" : function(e){
23787                 if(!e.shiftKey){
23788                     this.selectNext(e.shiftKey);
23789                 }else if(this.last !== false && this.lastActive !== false){
23790                     var last = this.last;
23791                     this.selectRange(this.last,  this.lastActive+1);
23792                     this.grid.getView().focusRow(this.lastActive);
23793                     if(last !== false){
23794                         this.last = last;
23795                     }
23796                 }else{
23797                     this.selectFirstRow();
23798                 }
23799                 this.fireEvent("afterselectionchange", this);
23800             },
23801             scope: this
23802         });
23803         this.grid.store.on('load', function(){
23804             this.selections.clear();
23805         },this);
23806         /*
23807         var view = this.grid.view;
23808         view.on("refresh", this.onRefresh, this);
23809         view.on("rowupdated", this.onRowUpdated, this);
23810         view.on("rowremoved", this.onRemove, this);
23811         */
23812     },
23813
23814     // private
23815     onRefresh : function()
23816     {
23817         var ds = this.grid.store, i, v = this.grid.view;
23818         var s = this.selections;
23819         s.each(function(r){
23820             if((i = ds.indexOfId(r.id)) != -1){
23821                 v.onRowSelect(i);
23822             }else{
23823                 s.remove(r);
23824             }
23825         });
23826     },
23827
23828     // private
23829     onRemove : function(v, index, r){
23830         this.selections.remove(r);
23831     },
23832
23833     // private
23834     onRowUpdated : function(v, index, r){
23835         if(this.isSelected(r)){
23836             v.onRowSelect(index);
23837         }
23838     },
23839
23840     /**
23841      * Select records.
23842      * @param {Array} records The records to select
23843      * @param {Boolean} keepExisting (optional) True to keep existing selections
23844      */
23845     selectRecords : function(records, keepExisting)
23846     {
23847         if(!keepExisting){
23848             this.clearSelections();
23849         }
23850             var ds = this.grid.store;
23851         for(var i = 0, len = records.length; i < len; i++){
23852             this.selectRow(ds.indexOf(records[i]), true);
23853         }
23854     },
23855
23856     /**
23857      * Gets the number of selected rows.
23858      * @return {Number}
23859      */
23860     getCount : function(){
23861         return this.selections.length;
23862     },
23863
23864     /**
23865      * Selects the first row in the grid.
23866      */
23867     selectFirstRow : function(){
23868         this.selectRow(0);
23869     },
23870
23871     /**
23872      * Select the last row.
23873      * @param {Boolean} keepExisting (optional) True to keep existing selections
23874      */
23875     selectLastRow : function(keepExisting){
23876         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23877         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23878     },
23879
23880     /**
23881      * Selects the row immediately following the last selected row.
23882      * @param {Boolean} keepExisting (optional) True to keep existing selections
23883      */
23884     selectNext : function(keepExisting)
23885     {
23886             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23887             this.selectRow(this.last+1, keepExisting);
23888             this.grid.getView().focusRow(this.last);
23889         }
23890     },
23891
23892     /**
23893      * Selects the row that precedes the last selected row.
23894      * @param {Boolean} keepExisting (optional) True to keep existing selections
23895      */
23896     selectPrevious : function(keepExisting){
23897         if(this.last){
23898             this.selectRow(this.last-1, keepExisting);
23899             this.grid.getView().focusRow(this.last);
23900         }
23901     },
23902
23903     /**
23904      * Returns the selected records
23905      * @return {Array} Array of selected records
23906      */
23907     getSelections : function(){
23908         return [].concat(this.selections.items);
23909     },
23910
23911     /**
23912      * Returns the first selected record.
23913      * @return {Record}
23914      */
23915     getSelected : function(){
23916         return this.selections.itemAt(0);
23917     },
23918
23919
23920     /**
23921      * Clears all selections.
23922      */
23923     clearSelections : function(fast)
23924     {
23925         if(this.locked) {
23926             return;
23927         }
23928         if(fast !== true){
23929                 var ds = this.grid.store;
23930             var s = this.selections;
23931             s.each(function(r){
23932                 this.deselectRow(ds.indexOfId(r.id));
23933             }, this);
23934             s.clear();
23935         }else{
23936             this.selections.clear();
23937         }
23938         this.last = false;
23939     },
23940
23941
23942     /**
23943      * Selects all rows.
23944      */
23945     selectAll : function(){
23946         if(this.locked) {
23947             return;
23948         }
23949         this.selections.clear();
23950         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23951             this.selectRow(i, true);
23952         }
23953     },
23954
23955     /**
23956      * Returns True if there is a selection.
23957      * @return {Boolean}
23958      */
23959     hasSelection : function(){
23960         return this.selections.length > 0;
23961     },
23962
23963     /**
23964      * Returns True if the specified row is selected.
23965      * @param {Number/Record} record The record or index of the record to check
23966      * @return {Boolean}
23967      */
23968     isSelected : function(index){
23969             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23970         return (r && this.selections.key(r.id) ? true : false);
23971     },
23972
23973     /**
23974      * Returns True if the specified record id is selected.
23975      * @param {String} id The id of record to check
23976      * @return {Boolean}
23977      */
23978     isIdSelected : function(id){
23979         return (this.selections.key(id) ? true : false);
23980     },
23981
23982
23983     // private
23984     handleMouseDBClick : function(e, t){
23985         
23986     },
23987     // private
23988     handleMouseDown : function(e, t)
23989     {
23990             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23991         if(this.isLocked() || rowIndex < 0 ){
23992             return;
23993         };
23994         if(e.shiftKey && this.last !== false){
23995             var last = this.last;
23996             this.selectRange(last, rowIndex, e.ctrlKey);
23997             this.last = last; // reset the last
23998             t.focus();
23999     
24000         }else{
24001             var isSelected = this.isSelected(rowIndex);
24002             //Roo.log("select row:" + rowIndex);
24003             if(isSelected){
24004                 this.deselectRow(rowIndex);
24005             } else {
24006                         this.selectRow(rowIndex, true);
24007             }
24008     
24009             /*
24010                 if(e.button !== 0 && isSelected){
24011                 alert('rowIndex 2: ' + rowIndex);
24012                     view.focusRow(rowIndex);
24013                 }else if(e.ctrlKey && isSelected){
24014                     this.deselectRow(rowIndex);
24015                 }else if(!isSelected){
24016                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24017                     view.focusRow(rowIndex);
24018                 }
24019             */
24020         }
24021         this.fireEvent("afterselectionchange", this);
24022     },
24023     // private
24024     handleDragableRowClick :  function(grid, rowIndex, e) 
24025     {
24026         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24027             this.selectRow(rowIndex, false);
24028             grid.view.focusRow(rowIndex);
24029              this.fireEvent("afterselectionchange", this);
24030         }
24031     },
24032     
24033     /**
24034      * Selects multiple rows.
24035      * @param {Array} rows Array of the indexes of the row to select
24036      * @param {Boolean} keepExisting (optional) True to keep existing selections
24037      */
24038     selectRows : function(rows, keepExisting){
24039         if(!keepExisting){
24040             this.clearSelections();
24041         }
24042         for(var i = 0, len = rows.length; i < len; i++){
24043             this.selectRow(rows[i], true);
24044         }
24045     },
24046
24047     /**
24048      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24049      * @param {Number} startRow The index of the first row in the range
24050      * @param {Number} endRow The index of the last row in the range
24051      * @param {Boolean} keepExisting (optional) True to retain existing selections
24052      */
24053     selectRange : function(startRow, endRow, keepExisting){
24054         if(this.locked) {
24055             return;
24056         }
24057         if(!keepExisting){
24058             this.clearSelections();
24059         }
24060         if(startRow <= endRow){
24061             for(var i = startRow; i <= endRow; i++){
24062                 this.selectRow(i, true);
24063             }
24064         }else{
24065             for(var i = startRow; i >= endRow; i--){
24066                 this.selectRow(i, true);
24067             }
24068         }
24069     },
24070
24071     /**
24072      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24073      * @param {Number} startRow The index of the first row in the range
24074      * @param {Number} endRow The index of the last row in the range
24075      */
24076     deselectRange : function(startRow, endRow, preventViewNotify){
24077         if(this.locked) {
24078             return;
24079         }
24080         for(var i = startRow; i <= endRow; i++){
24081             this.deselectRow(i, preventViewNotify);
24082         }
24083     },
24084
24085     /**
24086      * Selects a row.
24087      * @param {Number} row The index of the row to select
24088      * @param {Boolean} keepExisting (optional) True to keep existing selections
24089      */
24090     selectRow : function(index, keepExisting, preventViewNotify)
24091     {
24092             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24093             return;
24094         }
24095         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24096             if(!keepExisting || this.singleSelect){
24097                 this.clearSelections();
24098             }
24099             
24100             var r = this.grid.store.getAt(index);
24101             //console.log('selectRow - record id :' + r.id);
24102             
24103             this.selections.add(r);
24104             this.last = this.lastActive = index;
24105             if(!preventViewNotify){
24106                 var proxy = new Roo.Element(
24107                                 this.grid.getRowDom(index)
24108                 );
24109                 proxy.addClass('bg-info info');
24110             }
24111             this.fireEvent("rowselect", this, index, r);
24112             this.fireEvent("selectionchange", this);
24113         }
24114     },
24115
24116     /**
24117      * Deselects a row.
24118      * @param {Number} row The index of the row to deselect
24119      */
24120     deselectRow : function(index, preventViewNotify)
24121     {
24122         if(this.locked) {
24123             return;
24124         }
24125         if(this.last == index){
24126             this.last = false;
24127         }
24128         if(this.lastActive == index){
24129             this.lastActive = false;
24130         }
24131         
24132         var r = this.grid.store.getAt(index);
24133         if (!r) {
24134             return;
24135         }
24136         
24137         this.selections.remove(r);
24138         //.console.log('deselectRow - record id :' + r.id);
24139         if(!preventViewNotify){
24140         
24141             var proxy = new Roo.Element(
24142                 this.grid.getRowDom(index)
24143             );
24144             proxy.removeClass('bg-info info');
24145         }
24146         this.fireEvent("rowdeselect", this, index);
24147         this.fireEvent("selectionchange", this);
24148     },
24149
24150     // private
24151     restoreLast : function(){
24152         if(this._last){
24153             this.last = this._last;
24154         }
24155     },
24156
24157     // private
24158     acceptsNav : function(row, col, cm){
24159         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24160     },
24161
24162     // private
24163     onEditorKey : function(field, e){
24164         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24165         if(k == e.TAB){
24166             e.stopEvent();
24167             ed.completeEdit();
24168             if(e.shiftKey){
24169                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24170             }else{
24171                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24172             }
24173         }else if(k == e.ENTER && !e.ctrlKey){
24174             e.stopEvent();
24175             ed.completeEdit();
24176             if(e.shiftKey){
24177                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24178             }else{
24179                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24180             }
24181         }else if(k == e.ESC){
24182             ed.cancelEdit();
24183         }
24184         if(newCell){
24185             g.startEditing(newCell[0], newCell[1]);
24186         }
24187     }
24188 });
24189 /*
24190  * Based on:
24191  * Ext JS Library 1.1.1
24192  * Copyright(c) 2006-2007, Ext JS, LLC.
24193  *
24194  * Originally Released Under LGPL - original licence link has changed is not relivant.
24195  *
24196  * Fork - LGPL
24197  * <script type="text/javascript">
24198  */
24199  
24200 /**
24201  * @class Roo.bootstrap.PagingToolbar
24202  * @extends Roo.bootstrap.NavSimplebar
24203  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24204  * @constructor
24205  * Create a new PagingToolbar
24206  * @param {Object} config The config object
24207  * @param {Roo.data.Store} store
24208  */
24209 Roo.bootstrap.PagingToolbar = function(config)
24210 {
24211     // old args format still supported... - xtype is prefered..
24212         // created from xtype...
24213     
24214     this.ds = config.dataSource;
24215     
24216     if (config.store && !this.ds) {
24217         this.store= Roo.factory(config.store, Roo.data);
24218         this.ds = this.store;
24219         this.ds.xmodule = this.xmodule || false;
24220     }
24221     
24222     this.toolbarItems = [];
24223     if (config.items) {
24224         this.toolbarItems = config.items;
24225     }
24226     
24227     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24228     
24229     this.cursor = 0;
24230     
24231     if (this.ds) { 
24232         this.bind(this.ds);
24233     }
24234     
24235     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24236     
24237 };
24238
24239 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24240     /**
24241      * @cfg {Roo.data.Store} dataSource
24242      * The underlying data store providing the paged data
24243      */
24244     /**
24245      * @cfg {String/HTMLElement/Element} container
24246      * container The id or element that will contain the toolbar
24247      */
24248     /**
24249      * @cfg {Boolean} displayInfo
24250      * True to display the displayMsg (defaults to false)
24251      */
24252     /**
24253      * @cfg {Number} pageSize
24254      * The number of records to display per page (defaults to 20)
24255      */
24256     pageSize: 20,
24257     /**
24258      * @cfg {String} displayMsg
24259      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24260      */
24261     displayMsg : 'Displaying {0} - {1} of {2}',
24262     /**
24263      * @cfg {String} emptyMsg
24264      * The message to display when no records are found (defaults to "No data to display")
24265      */
24266     emptyMsg : 'No data to display',
24267     /**
24268      * Customizable piece of the default paging text (defaults to "Page")
24269      * @type String
24270      */
24271     beforePageText : "Page",
24272     /**
24273      * Customizable piece of the default paging text (defaults to "of %0")
24274      * @type String
24275      */
24276     afterPageText : "of {0}",
24277     /**
24278      * Customizable piece of the default paging text (defaults to "First Page")
24279      * @type String
24280      */
24281     firstText : "First Page",
24282     /**
24283      * Customizable piece of the default paging text (defaults to "Previous Page")
24284      * @type String
24285      */
24286     prevText : "Previous Page",
24287     /**
24288      * Customizable piece of the default paging text (defaults to "Next Page")
24289      * @type String
24290      */
24291     nextText : "Next Page",
24292     /**
24293      * Customizable piece of the default paging text (defaults to "Last Page")
24294      * @type String
24295      */
24296     lastText : "Last Page",
24297     /**
24298      * Customizable piece of the default paging text (defaults to "Refresh")
24299      * @type String
24300      */
24301     refreshText : "Refresh",
24302
24303     buttons : false,
24304     // private
24305     onRender : function(ct, position) 
24306     {
24307         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24308         this.navgroup.parentId = this.id;
24309         this.navgroup.onRender(this.el, null);
24310         // add the buttons to the navgroup
24311         
24312         if(this.displayInfo){
24313             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24314             this.displayEl = this.el.select('.x-paging-info', true).first();
24315 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24316 //            this.displayEl = navel.el.select('span',true).first();
24317         }
24318         
24319         var _this = this;
24320         
24321         if(this.buttons){
24322             Roo.each(_this.buttons, function(e){ // this might need to use render????
24323                Roo.factory(e).onRender(_this.el, null);
24324             });
24325         }
24326             
24327         Roo.each(_this.toolbarItems, function(e) {
24328             _this.navgroup.addItem(e);
24329         });
24330         
24331         
24332         this.first = this.navgroup.addItem({
24333             tooltip: this.firstText,
24334             cls: "prev",
24335             icon : 'fa fa-backward',
24336             disabled: true,
24337             preventDefault: true,
24338             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24339         });
24340         
24341         this.prev =  this.navgroup.addItem({
24342             tooltip: this.prevText,
24343             cls: "prev",
24344             icon : 'fa fa-step-backward',
24345             disabled: true,
24346             preventDefault: true,
24347             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24348         });
24349     //this.addSeparator();
24350         
24351         
24352         var field = this.navgroup.addItem( {
24353             tagtype : 'span',
24354             cls : 'x-paging-position',
24355             
24356             html : this.beforePageText  +
24357                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24358                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24359          } ); //?? escaped?
24360         
24361         this.field = field.el.select('input', true).first();
24362         this.field.on("keydown", this.onPagingKeydown, this);
24363         this.field.on("focus", function(){this.dom.select();});
24364     
24365     
24366         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24367         //this.field.setHeight(18);
24368         //this.addSeparator();
24369         this.next = this.navgroup.addItem({
24370             tooltip: this.nextText,
24371             cls: "next",
24372             html : ' <i class="fa fa-step-forward">',
24373             disabled: true,
24374             preventDefault: true,
24375             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24376         });
24377         this.last = this.navgroup.addItem({
24378             tooltip: this.lastText,
24379             icon : 'fa fa-forward',
24380             cls: "next",
24381             disabled: true,
24382             preventDefault: true,
24383             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24384         });
24385     //this.addSeparator();
24386         this.loading = this.navgroup.addItem({
24387             tooltip: this.refreshText,
24388             icon: 'fa fa-refresh',
24389             preventDefault: true,
24390             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24391         });
24392         
24393     },
24394
24395     // private
24396     updateInfo : function(){
24397         if(this.displayEl){
24398             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24399             var msg = count == 0 ?
24400                 this.emptyMsg :
24401                 String.format(
24402                     this.displayMsg,
24403                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24404                 );
24405             this.displayEl.update(msg);
24406         }
24407     },
24408
24409     // private
24410     onLoad : function(ds, r, o)
24411     {
24412         this.cursor = o.params ? o.params.start : 0;
24413         var d = this.getPageData(),
24414             ap = d.activePage,
24415             ps = d.pages;
24416         
24417         
24418         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24419         this.field.dom.value = ap;
24420         this.first.setDisabled(ap == 1);
24421         this.prev.setDisabled(ap == 1);
24422         this.next.setDisabled(ap == ps);
24423         this.last.setDisabled(ap == ps);
24424         this.loading.enable();
24425         this.updateInfo();
24426     },
24427
24428     // private
24429     getPageData : function(){
24430         var total = this.ds.getTotalCount();
24431         return {
24432             total : total,
24433             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24434             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24435         };
24436     },
24437
24438     // private
24439     onLoadError : function(){
24440         this.loading.enable();
24441     },
24442
24443     // private
24444     onPagingKeydown : function(e){
24445         var k = e.getKey();
24446         var d = this.getPageData();
24447         if(k == e.RETURN){
24448             var v = this.field.dom.value, pageNum;
24449             if(!v || isNaN(pageNum = parseInt(v, 10))){
24450                 this.field.dom.value = d.activePage;
24451                 return;
24452             }
24453             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24454             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24455             e.stopEvent();
24456         }
24457         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))
24458         {
24459           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24460           this.field.dom.value = pageNum;
24461           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24462           e.stopEvent();
24463         }
24464         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24465         {
24466           var v = this.field.dom.value, pageNum; 
24467           var increment = (e.shiftKey) ? 10 : 1;
24468           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24469                 increment *= -1;
24470           }
24471           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24472             this.field.dom.value = d.activePage;
24473             return;
24474           }
24475           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24476           {
24477             this.field.dom.value = parseInt(v, 10) + increment;
24478             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24479             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24480           }
24481           e.stopEvent();
24482         }
24483     },
24484
24485     // private
24486     beforeLoad : function(){
24487         if(this.loading){
24488             this.loading.disable();
24489         }
24490     },
24491
24492     // private
24493     onClick : function(which){
24494         
24495         var ds = this.ds;
24496         if (!ds) {
24497             return;
24498         }
24499         
24500         switch(which){
24501             case "first":
24502                 ds.load({params:{start: 0, limit: this.pageSize}});
24503             break;
24504             case "prev":
24505                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24506             break;
24507             case "next":
24508                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24509             break;
24510             case "last":
24511                 var total = ds.getTotalCount();
24512                 var extra = total % this.pageSize;
24513                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24514                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24515             break;
24516             case "refresh":
24517                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24518             break;
24519         }
24520     },
24521
24522     /**
24523      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24524      * @param {Roo.data.Store} store The data store to unbind
24525      */
24526     unbind : function(ds){
24527         ds.un("beforeload", this.beforeLoad, this);
24528         ds.un("load", this.onLoad, this);
24529         ds.un("loadexception", this.onLoadError, this);
24530         ds.un("remove", this.updateInfo, this);
24531         ds.un("add", this.updateInfo, this);
24532         this.ds = undefined;
24533     },
24534
24535     /**
24536      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24537      * @param {Roo.data.Store} store The data store to bind
24538      */
24539     bind : function(ds){
24540         ds.on("beforeload", this.beforeLoad, this);
24541         ds.on("load", this.onLoad, this);
24542         ds.on("loadexception", this.onLoadError, this);
24543         ds.on("remove", this.updateInfo, this);
24544         ds.on("add", this.updateInfo, this);
24545         this.ds = ds;
24546     }
24547 });/*
24548  * - LGPL
24549  *
24550  * element
24551  * 
24552  */
24553
24554 /**
24555  * @class Roo.bootstrap.MessageBar
24556  * @extends Roo.bootstrap.Component
24557  * Bootstrap MessageBar class
24558  * @cfg {String} html contents of the MessageBar
24559  * @cfg {String} weight (info | success | warning | danger) default info
24560  * @cfg {String} beforeClass insert the bar before the given class
24561  * @cfg {Boolean} closable (true | false) default false
24562  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24563  * 
24564  * @constructor
24565  * Create a new Element
24566  * @param {Object} config The config object
24567  */
24568
24569 Roo.bootstrap.MessageBar = function(config){
24570     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24571 };
24572
24573 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24574     
24575     html: '',
24576     weight: 'info',
24577     closable: false,
24578     fixed: false,
24579     beforeClass: 'bootstrap-sticky-wrap',
24580     
24581     getAutoCreate : function(){
24582         
24583         var cfg = {
24584             tag: 'div',
24585             cls: 'alert alert-dismissable alert-' + this.weight,
24586             cn: [
24587                 {
24588                     tag: 'span',
24589                     cls: 'message',
24590                     html: this.html || ''
24591                 }
24592             ]
24593         };
24594         
24595         if(this.fixed){
24596             cfg.cls += ' alert-messages-fixed';
24597         }
24598         
24599         if(this.closable){
24600             cfg.cn.push({
24601                 tag: 'button',
24602                 cls: 'close',
24603                 html: 'x'
24604             });
24605         }
24606         
24607         return cfg;
24608     },
24609     
24610     onRender : function(ct, position)
24611     {
24612         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24613         
24614         if(!this.el){
24615             var cfg = Roo.apply({},  this.getAutoCreate());
24616             cfg.id = Roo.id();
24617             
24618             if (this.cls) {
24619                 cfg.cls += ' ' + this.cls;
24620             }
24621             if (this.style) {
24622                 cfg.style = this.style;
24623             }
24624             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24625             
24626             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24627         }
24628         
24629         this.el.select('>button.close').on('click', this.hide, this);
24630         
24631     },
24632     
24633     show : function()
24634     {
24635         if (!this.rendered) {
24636             this.render();
24637         }
24638         
24639         this.el.show();
24640         
24641         this.fireEvent('show', this);
24642         
24643     },
24644     
24645     hide : function()
24646     {
24647         if (!this.rendered) {
24648             this.render();
24649         }
24650         
24651         this.el.hide();
24652         
24653         this.fireEvent('hide', this);
24654     },
24655     
24656     update : function()
24657     {
24658 //        var e = this.el.dom.firstChild;
24659 //        
24660 //        if(this.closable){
24661 //            e = e.nextSibling;
24662 //        }
24663 //        
24664 //        e.data = this.html || '';
24665
24666         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24667     }
24668    
24669 });
24670
24671  
24672
24673      /*
24674  * - LGPL
24675  *
24676  * Graph
24677  * 
24678  */
24679
24680
24681 /**
24682  * @class Roo.bootstrap.Graph
24683  * @extends Roo.bootstrap.Component
24684  * Bootstrap Graph class
24685 > Prameters
24686  -sm {number} sm 4
24687  -md {number} md 5
24688  @cfg {String} graphtype  bar | vbar | pie
24689  @cfg {number} g_x coodinator | centre x (pie)
24690  @cfg {number} g_y coodinator | centre y (pie)
24691  @cfg {number} g_r radius (pie)
24692  @cfg {number} g_height height of the chart (respected by all elements in the set)
24693  @cfg {number} g_width width of the chart (respected by all elements in the set)
24694  @cfg {Object} title The title of the chart
24695     
24696  -{Array}  values
24697  -opts (object) options for the chart 
24698      o {
24699      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24700      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24701      o vgutter (number)
24702      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.
24703      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24704      o to
24705      o stretch (boolean)
24706      o }
24707  -opts (object) options for the pie
24708      o{
24709      o cut
24710      o startAngle (number)
24711      o endAngle (number)
24712      } 
24713  *
24714  * @constructor
24715  * Create a new Input
24716  * @param {Object} config The config object
24717  */
24718
24719 Roo.bootstrap.Graph = function(config){
24720     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24721     
24722     this.addEvents({
24723         // img events
24724         /**
24725          * @event click
24726          * The img click event for the img.
24727          * @param {Roo.EventObject} e
24728          */
24729         "click" : true
24730     });
24731 };
24732
24733 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24734     
24735     sm: 4,
24736     md: 5,
24737     graphtype: 'bar',
24738     g_height: 250,
24739     g_width: 400,
24740     g_x: 50,
24741     g_y: 50,
24742     g_r: 30,
24743     opts:{
24744         //g_colors: this.colors,
24745         g_type: 'soft',
24746         g_gutter: '20%'
24747
24748     },
24749     title : false,
24750
24751     getAutoCreate : function(){
24752         
24753         var cfg = {
24754             tag: 'div',
24755             html : null
24756         };
24757         
24758         
24759         return  cfg;
24760     },
24761
24762     onRender : function(ct,position){
24763         
24764         
24765         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24766         
24767         if (typeof(Raphael) == 'undefined') {
24768             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24769             return;
24770         }
24771         
24772         this.raphael = Raphael(this.el.dom);
24773         
24774                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24775                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24776                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24777                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24778                 /*
24779                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24780                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24781                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24782                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24783                 
24784                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24785                 r.barchart(330, 10, 300, 220, data1);
24786                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24787                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24788                 */
24789                 
24790                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24791                 // r.barchart(30, 30, 560, 250,  xdata, {
24792                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24793                 //     axis : "0 0 1 1",
24794                 //     axisxlabels :  xdata
24795                 //     //yvalues : cols,
24796                    
24797                 // });
24798 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24799 //        
24800 //        this.load(null,xdata,{
24801 //                axis : "0 0 1 1",
24802 //                axisxlabels :  xdata
24803 //                });
24804
24805     },
24806
24807     load : function(graphtype,xdata,opts)
24808     {
24809         this.raphael.clear();
24810         if(!graphtype) {
24811             graphtype = this.graphtype;
24812         }
24813         if(!opts){
24814             opts = this.opts;
24815         }
24816         var r = this.raphael,
24817             fin = function () {
24818                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24819             },
24820             fout = function () {
24821                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24822             },
24823             pfin = function() {
24824                 this.sector.stop();
24825                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24826
24827                 if (this.label) {
24828                     this.label[0].stop();
24829                     this.label[0].attr({ r: 7.5 });
24830                     this.label[1].attr({ "font-weight": 800 });
24831                 }
24832             },
24833             pfout = function() {
24834                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24835
24836                 if (this.label) {
24837                     this.label[0].animate({ r: 5 }, 500, "bounce");
24838                     this.label[1].attr({ "font-weight": 400 });
24839                 }
24840             };
24841
24842         switch(graphtype){
24843             case 'bar':
24844                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24845                 break;
24846             case 'hbar':
24847                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24848                 break;
24849             case 'pie':
24850 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24851 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24852 //            
24853                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24854                 
24855                 break;
24856
24857         }
24858         
24859         if(this.title){
24860             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24861         }
24862         
24863     },
24864     
24865     setTitle: function(o)
24866     {
24867         this.title = o;
24868     },
24869     
24870     initEvents: function() {
24871         
24872         if(!this.href){
24873             this.el.on('click', this.onClick, this);
24874         }
24875     },
24876     
24877     onClick : function(e)
24878     {
24879         Roo.log('img onclick');
24880         this.fireEvent('click', this, e);
24881     }
24882    
24883 });
24884
24885  
24886 /*
24887  * - LGPL
24888  *
24889  * numberBox
24890  * 
24891  */
24892 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24893
24894 /**
24895  * @class Roo.bootstrap.dash.NumberBox
24896  * @extends Roo.bootstrap.Component
24897  * Bootstrap NumberBox class
24898  * @cfg {String} headline Box headline
24899  * @cfg {String} content Box content
24900  * @cfg {String} icon Box icon
24901  * @cfg {String} footer Footer text
24902  * @cfg {String} fhref Footer href
24903  * 
24904  * @constructor
24905  * Create a new NumberBox
24906  * @param {Object} config The config object
24907  */
24908
24909
24910 Roo.bootstrap.dash.NumberBox = function(config){
24911     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24912     
24913 };
24914
24915 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24916     
24917     headline : '',
24918     content : '',
24919     icon : '',
24920     footer : '',
24921     fhref : '',
24922     ficon : '',
24923     
24924     getAutoCreate : function(){
24925         
24926         var cfg = {
24927             tag : 'div',
24928             cls : 'small-box ',
24929             cn : [
24930                 {
24931                     tag : 'div',
24932                     cls : 'inner',
24933                     cn :[
24934                         {
24935                             tag : 'h3',
24936                             cls : 'roo-headline',
24937                             html : this.headline
24938                         },
24939                         {
24940                             tag : 'p',
24941                             cls : 'roo-content',
24942                             html : this.content
24943                         }
24944                     ]
24945                 }
24946             ]
24947         };
24948         
24949         if(this.icon){
24950             cfg.cn.push({
24951                 tag : 'div',
24952                 cls : 'icon',
24953                 cn :[
24954                     {
24955                         tag : 'i',
24956                         cls : 'ion ' + this.icon
24957                     }
24958                 ]
24959             });
24960         }
24961         
24962         if(this.footer){
24963             var footer = {
24964                 tag : 'a',
24965                 cls : 'small-box-footer',
24966                 href : this.fhref || '#',
24967                 html : this.footer
24968             };
24969             
24970             cfg.cn.push(footer);
24971             
24972         }
24973         
24974         return  cfg;
24975     },
24976
24977     onRender : function(ct,position){
24978         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24979
24980
24981        
24982                 
24983     },
24984
24985     setHeadline: function (value)
24986     {
24987         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24988     },
24989     
24990     setFooter: function (value, href)
24991     {
24992         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24993         
24994         if(href){
24995             this.el.select('a.small-box-footer',true).first().attr('href', href);
24996         }
24997         
24998     },
24999
25000     setContent: function (value)
25001     {
25002         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25003     },
25004
25005     initEvents: function() 
25006     {   
25007         
25008     }
25009     
25010 });
25011
25012  
25013 /*
25014  * - LGPL
25015  *
25016  * TabBox
25017  * 
25018  */
25019 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25020
25021 /**
25022  * @class Roo.bootstrap.dash.TabBox
25023  * @extends Roo.bootstrap.Component
25024  * Bootstrap TabBox class
25025  * @cfg {String} title Title of the TabBox
25026  * @cfg {String} icon Icon of the TabBox
25027  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25028  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25029  * 
25030  * @constructor
25031  * Create a new TabBox
25032  * @param {Object} config The config object
25033  */
25034
25035
25036 Roo.bootstrap.dash.TabBox = function(config){
25037     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25038     this.addEvents({
25039         // raw events
25040         /**
25041          * @event addpane
25042          * When a pane is added
25043          * @param {Roo.bootstrap.dash.TabPane} pane
25044          */
25045         "addpane" : true,
25046         /**
25047          * @event activatepane
25048          * When a pane is activated
25049          * @param {Roo.bootstrap.dash.TabPane} pane
25050          */
25051         "activatepane" : true
25052         
25053          
25054     });
25055     
25056     this.panes = [];
25057 };
25058
25059 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25060
25061     title : '',
25062     icon : false,
25063     showtabs : true,
25064     tabScrollable : false,
25065     
25066     getChildContainer : function()
25067     {
25068         return this.el.select('.tab-content', true).first();
25069     },
25070     
25071     getAutoCreate : function(){
25072         
25073         var header = {
25074             tag: 'li',
25075             cls: 'pull-left header',
25076             html: this.title,
25077             cn : []
25078         };
25079         
25080         if(this.icon){
25081             header.cn.push({
25082                 tag: 'i',
25083                 cls: 'fa ' + this.icon
25084             });
25085         }
25086         
25087         var h = {
25088             tag: 'ul',
25089             cls: 'nav nav-tabs pull-right',
25090             cn: [
25091                 header
25092             ]
25093         };
25094         
25095         if(this.tabScrollable){
25096             h = {
25097                 tag: 'div',
25098                 cls: 'tab-header',
25099                 cn: [
25100                     {
25101                         tag: 'ul',
25102                         cls: 'nav nav-tabs pull-right',
25103                         cn: [
25104                             header
25105                         ]
25106                     }
25107                 ]
25108             };
25109         }
25110         
25111         var cfg = {
25112             tag: 'div',
25113             cls: 'nav-tabs-custom',
25114             cn: [
25115                 h,
25116                 {
25117                     tag: 'div',
25118                     cls: 'tab-content no-padding',
25119                     cn: []
25120                 }
25121             ]
25122         };
25123
25124         return  cfg;
25125     },
25126     initEvents : function()
25127     {
25128         //Roo.log('add add pane handler');
25129         this.on('addpane', this.onAddPane, this);
25130     },
25131      /**
25132      * Updates the box title
25133      * @param {String} html to set the title to.
25134      */
25135     setTitle : function(value)
25136     {
25137         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25138     },
25139     onAddPane : function(pane)
25140     {
25141         this.panes.push(pane);
25142         //Roo.log('addpane');
25143         //Roo.log(pane);
25144         // tabs are rendere left to right..
25145         if(!this.showtabs){
25146             return;
25147         }
25148         
25149         var ctr = this.el.select('.nav-tabs', true).first();
25150          
25151          
25152         var existing = ctr.select('.nav-tab',true);
25153         var qty = existing.getCount();;
25154         
25155         
25156         var tab = ctr.createChild({
25157             tag : 'li',
25158             cls : 'nav-tab' + (qty ? '' : ' active'),
25159             cn : [
25160                 {
25161                     tag : 'a',
25162                     href:'#',
25163                     html : pane.title
25164                 }
25165             ]
25166         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25167         pane.tab = tab;
25168         
25169         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25170         if (!qty) {
25171             pane.el.addClass('active');
25172         }
25173         
25174                 
25175     },
25176     onTabClick : function(ev,un,ob,pane)
25177     {
25178         //Roo.log('tab - prev default');
25179         ev.preventDefault();
25180         
25181         
25182         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25183         pane.tab.addClass('active');
25184         //Roo.log(pane.title);
25185         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25186         // technically we should have a deactivate event.. but maybe add later.
25187         // and it should not de-activate the selected tab...
25188         this.fireEvent('activatepane', pane);
25189         pane.el.addClass('active');
25190         pane.fireEvent('activate');
25191         
25192         
25193     },
25194     
25195     getActivePane : function()
25196     {
25197         var r = false;
25198         Roo.each(this.panes, function(p) {
25199             if(p.el.hasClass('active')){
25200                 r = p;
25201                 return false;
25202             }
25203             
25204             return;
25205         });
25206         
25207         return r;
25208     }
25209     
25210     
25211 });
25212
25213  
25214 /*
25215  * - LGPL
25216  *
25217  * Tab pane
25218  * 
25219  */
25220 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25221 /**
25222  * @class Roo.bootstrap.TabPane
25223  * @extends Roo.bootstrap.Component
25224  * Bootstrap TabPane class
25225  * @cfg {Boolean} active (false | true) Default false
25226  * @cfg {String} title title of panel
25227
25228  * 
25229  * @constructor
25230  * Create a new TabPane
25231  * @param {Object} config The config object
25232  */
25233
25234 Roo.bootstrap.dash.TabPane = function(config){
25235     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25236     
25237     this.addEvents({
25238         // raw events
25239         /**
25240          * @event activate
25241          * When a pane is activated
25242          * @param {Roo.bootstrap.dash.TabPane} pane
25243          */
25244         "activate" : true
25245          
25246     });
25247 };
25248
25249 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25250     
25251     active : false,
25252     title : '',
25253     
25254     // the tabBox that this is attached to.
25255     tab : false,
25256      
25257     getAutoCreate : function() 
25258     {
25259         var cfg = {
25260             tag: 'div',
25261             cls: 'tab-pane'
25262         };
25263         
25264         if(this.active){
25265             cfg.cls += ' active';
25266         }
25267         
25268         return cfg;
25269     },
25270     initEvents  : function()
25271     {
25272         //Roo.log('trigger add pane handler');
25273         this.parent().fireEvent('addpane', this)
25274     },
25275     
25276      /**
25277      * Updates the tab title 
25278      * @param {String} html to set the title to.
25279      */
25280     setTitle: function(str)
25281     {
25282         if (!this.tab) {
25283             return;
25284         }
25285         this.title = str;
25286         this.tab.select('a', true).first().dom.innerHTML = str;
25287         
25288     }
25289     
25290     
25291     
25292 });
25293
25294  
25295
25296
25297  /*
25298  * - LGPL
25299  *
25300  * menu
25301  * 
25302  */
25303 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25304
25305 /**
25306  * @class Roo.bootstrap.menu.Menu
25307  * @extends Roo.bootstrap.Component
25308  * Bootstrap Menu class - container for Menu
25309  * @cfg {String} html Text of the menu
25310  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25311  * @cfg {String} icon Font awesome icon
25312  * @cfg {String} pos Menu align to (top | bottom) default bottom
25313  * 
25314  * 
25315  * @constructor
25316  * Create a new Menu
25317  * @param {Object} config The config object
25318  */
25319
25320
25321 Roo.bootstrap.menu.Menu = function(config){
25322     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25323     
25324     this.addEvents({
25325         /**
25326          * @event beforeshow
25327          * Fires before this menu is displayed
25328          * @param {Roo.bootstrap.menu.Menu} this
25329          */
25330         beforeshow : true,
25331         /**
25332          * @event beforehide
25333          * Fires before this menu is hidden
25334          * @param {Roo.bootstrap.menu.Menu} this
25335          */
25336         beforehide : true,
25337         /**
25338          * @event show
25339          * Fires after this menu is displayed
25340          * @param {Roo.bootstrap.menu.Menu} this
25341          */
25342         show : true,
25343         /**
25344          * @event hide
25345          * Fires after this menu is hidden
25346          * @param {Roo.bootstrap.menu.Menu} this
25347          */
25348         hide : true,
25349         /**
25350          * @event click
25351          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25352          * @param {Roo.bootstrap.menu.Menu} this
25353          * @param {Roo.EventObject} e
25354          */
25355         click : true
25356     });
25357     
25358 };
25359
25360 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25361     
25362     submenu : false,
25363     html : '',
25364     weight : 'default',
25365     icon : false,
25366     pos : 'bottom',
25367     
25368     
25369     getChildContainer : function() {
25370         if(this.isSubMenu){
25371             return this.el;
25372         }
25373         
25374         return this.el.select('ul.dropdown-menu', true).first();  
25375     },
25376     
25377     getAutoCreate : function()
25378     {
25379         var text = [
25380             {
25381                 tag : 'span',
25382                 cls : 'roo-menu-text',
25383                 html : this.html
25384             }
25385         ];
25386         
25387         if(this.icon){
25388             text.unshift({
25389                 tag : 'i',
25390                 cls : 'fa ' + this.icon
25391             })
25392         }
25393         
25394         
25395         var cfg = {
25396             tag : 'div',
25397             cls : 'btn-group',
25398             cn : [
25399                 {
25400                     tag : 'button',
25401                     cls : 'dropdown-button btn btn-' + this.weight,
25402                     cn : text
25403                 },
25404                 {
25405                     tag : 'button',
25406                     cls : 'dropdown-toggle btn btn-' + this.weight,
25407                     cn : [
25408                         {
25409                             tag : 'span',
25410                             cls : 'caret'
25411                         }
25412                     ]
25413                 },
25414                 {
25415                     tag : 'ul',
25416                     cls : 'dropdown-menu'
25417                 }
25418             ]
25419             
25420         };
25421         
25422         if(this.pos == 'top'){
25423             cfg.cls += ' dropup';
25424         }
25425         
25426         if(this.isSubMenu){
25427             cfg = {
25428                 tag : 'ul',
25429                 cls : 'dropdown-menu'
25430             }
25431         }
25432         
25433         return cfg;
25434     },
25435     
25436     onRender : function(ct, position)
25437     {
25438         this.isSubMenu = ct.hasClass('dropdown-submenu');
25439         
25440         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25441     },
25442     
25443     initEvents : function() 
25444     {
25445         if(this.isSubMenu){
25446             return;
25447         }
25448         
25449         this.hidden = true;
25450         
25451         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25452         this.triggerEl.on('click', this.onTriggerPress, this);
25453         
25454         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25455         this.buttonEl.on('click', this.onClick, this);
25456         
25457     },
25458     
25459     list : function()
25460     {
25461         if(this.isSubMenu){
25462             return this.el;
25463         }
25464         
25465         return this.el.select('ul.dropdown-menu', true).first();
25466     },
25467     
25468     onClick : function(e)
25469     {
25470         this.fireEvent("click", this, e);
25471     },
25472     
25473     onTriggerPress  : function(e)
25474     {   
25475         if (this.isVisible()) {
25476             this.hide();
25477         } else {
25478             this.show();
25479         }
25480     },
25481     
25482     isVisible : function(){
25483         return !this.hidden;
25484     },
25485     
25486     show : function()
25487     {
25488         this.fireEvent("beforeshow", this);
25489         
25490         this.hidden = false;
25491         this.el.addClass('open');
25492         
25493         Roo.get(document).on("mouseup", this.onMouseUp, this);
25494         
25495         this.fireEvent("show", this);
25496         
25497         
25498     },
25499     
25500     hide : function()
25501     {
25502         this.fireEvent("beforehide", this);
25503         
25504         this.hidden = true;
25505         this.el.removeClass('open');
25506         
25507         Roo.get(document).un("mouseup", this.onMouseUp);
25508         
25509         this.fireEvent("hide", this);
25510     },
25511     
25512     onMouseUp : function()
25513     {
25514         this.hide();
25515     }
25516     
25517 });
25518
25519  
25520  /*
25521  * - LGPL
25522  *
25523  * menu item
25524  * 
25525  */
25526 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25527
25528 /**
25529  * @class Roo.bootstrap.menu.Item
25530  * @extends Roo.bootstrap.Component
25531  * Bootstrap MenuItem class
25532  * @cfg {Boolean} submenu (true | false) default false
25533  * @cfg {String} html text of the item
25534  * @cfg {String} href the link
25535  * @cfg {Boolean} disable (true | false) default false
25536  * @cfg {Boolean} preventDefault (true | false) default true
25537  * @cfg {String} icon Font awesome icon
25538  * @cfg {String} pos Submenu align to (left | right) default right 
25539  * 
25540  * 
25541  * @constructor
25542  * Create a new Item
25543  * @param {Object} config The config object
25544  */
25545
25546
25547 Roo.bootstrap.menu.Item = function(config){
25548     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25549     this.addEvents({
25550         /**
25551          * @event mouseover
25552          * Fires when the mouse is hovering over this menu
25553          * @param {Roo.bootstrap.menu.Item} this
25554          * @param {Roo.EventObject} e
25555          */
25556         mouseover : true,
25557         /**
25558          * @event mouseout
25559          * Fires when the mouse exits this menu
25560          * @param {Roo.bootstrap.menu.Item} this
25561          * @param {Roo.EventObject} e
25562          */
25563         mouseout : true,
25564         // raw events
25565         /**
25566          * @event click
25567          * The raw click event for the entire grid.
25568          * @param {Roo.EventObject} e
25569          */
25570         click : true
25571     });
25572 };
25573
25574 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25575     
25576     submenu : false,
25577     href : '',
25578     html : '',
25579     preventDefault: true,
25580     disable : false,
25581     icon : false,
25582     pos : 'right',
25583     
25584     getAutoCreate : function()
25585     {
25586         var text = [
25587             {
25588                 tag : 'span',
25589                 cls : 'roo-menu-item-text',
25590                 html : this.html
25591             }
25592         ];
25593         
25594         if(this.icon){
25595             text.unshift({
25596                 tag : 'i',
25597                 cls : 'fa ' + this.icon
25598             })
25599         }
25600         
25601         var cfg = {
25602             tag : 'li',
25603             cn : [
25604                 {
25605                     tag : 'a',
25606                     href : this.href || '#',
25607                     cn : text
25608                 }
25609             ]
25610         };
25611         
25612         if(this.disable){
25613             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25614         }
25615         
25616         if(this.submenu){
25617             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25618             
25619             if(this.pos == 'left'){
25620                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25621             }
25622         }
25623         
25624         return cfg;
25625     },
25626     
25627     initEvents : function() 
25628     {
25629         this.el.on('mouseover', this.onMouseOver, this);
25630         this.el.on('mouseout', this.onMouseOut, this);
25631         
25632         this.el.select('a', true).first().on('click', this.onClick, this);
25633         
25634     },
25635     
25636     onClick : function(e)
25637     {
25638         if(this.preventDefault){
25639             e.preventDefault();
25640         }
25641         
25642         this.fireEvent("click", this, e);
25643     },
25644     
25645     onMouseOver : function(e)
25646     {
25647         if(this.submenu && this.pos == 'left'){
25648             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25649         }
25650         
25651         this.fireEvent("mouseover", this, e);
25652     },
25653     
25654     onMouseOut : function(e)
25655     {
25656         this.fireEvent("mouseout", this, e);
25657     }
25658 });
25659
25660  
25661
25662  /*
25663  * - LGPL
25664  *
25665  * menu separator
25666  * 
25667  */
25668 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25669
25670 /**
25671  * @class Roo.bootstrap.menu.Separator
25672  * @extends Roo.bootstrap.Component
25673  * Bootstrap Separator class
25674  * 
25675  * @constructor
25676  * Create a new Separator
25677  * @param {Object} config The config object
25678  */
25679
25680
25681 Roo.bootstrap.menu.Separator = function(config){
25682     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25683 };
25684
25685 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25686     
25687     getAutoCreate : function(){
25688         var cfg = {
25689             tag : 'li',
25690             cls: 'divider'
25691         };
25692         
25693         return cfg;
25694     }
25695    
25696 });
25697
25698  
25699
25700  /*
25701  * - LGPL
25702  *
25703  * Tooltip
25704  * 
25705  */
25706
25707 /**
25708  * @class Roo.bootstrap.Tooltip
25709  * Bootstrap Tooltip class
25710  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25711  * to determine which dom element triggers the tooltip.
25712  * 
25713  * It needs to add support for additional attributes like tooltip-position
25714  * 
25715  * @constructor
25716  * Create a new Toolti
25717  * @param {Object} config The config object
25718  */
25719
25720 Roo.bootstrap.Tooltip = function(config){
25721     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25722     
25723     this.alignment = Roo.bootstrap.Tooltip.alignment;
25724     
25725     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25726         this.alignment = config.alignment;
25727     }
25728     
25729 };
25730
25731 Roo.apply(Roo.bootstrap.Tooltip, {
25732     /**
25733      * @function init initialize tooltip monitoring.
25734      * @static
25735      */
25736     currentEl : false,
25737     currentTip : false,
25738     currentRegion : false,
25739     
25740     //  init : delay?
25741     
25742     init : function()
25743     {
25744         Roo.get(document).on('mouseover', this.enter ,this);
25745         Roo.get(document).on('mouseout', this.leave, this);
25746          
25747         
25748         this.currentTip = new Roo.bootstrap.Tooltip();
25749     },
25750     
25751     enter : function(ev)
25752     {
25753         var dom = ev.getTarget();
25754         
25755         //Roo.log(['enter',dom]);
25756         var el = Roo.fly(dom);
25757         if (this.currentEl) {
25758             //Roo.log(dom);
25759             //Roo.log(this.currentEl);
25760             //Roo.log(this.currentEl.contains(dom));
25761             if (this.currentEl == el) {
25762                 return;
25763             }
25764             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25765                 return;
25766             }
25767
25768         }
25769         
25770         if (this.currentTip.el) {
25771             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25772         }    
25773         //Roo.log(ev);
25774         
25775         if(!el || el.dom == document){
25776             return;
25777         }
25778         
25779         var bindEl = el;
25780         
25781         // you can not look for children, as if el is the body.. then everythign is the child..
25782         if (!el.attr('tooltip')) { //
25783             if (!el.select("[tooltip]").elements.length) {
25784                 return;
25785             }
25786             // is the mouse over this child...?
25787             bindEl = el.select("[tooltip]").first();
25788             var xy = ev.getXY();
25789             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25790                 //Roo.log("not in region.");
25791                 return;
25792             }
25793             //Roo.log("child element over..");
25794             
25795         }
25796         this.currentEl = bindEl;
25797         this.currentTip.bind(bindEl);
25798         this.currentRegion = Roo.lib.Region.getRegion(dom);
25799         this.currentTip.enter();
25800         
25801     },
25802     leave : function(ev)
25803     {
25804         var dom = ev.getTarget();
25805         //Roo.log(['leave',dom]);
25806         if (!this.currentEl) {
25807             return;
25808         }
25809         
25810         
25811         if (dom != this.currentEl.dom) {
25812             return;
25813         }
25814         var xy = ev.getXY();
25815         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25816             return;
25817         }
25818         // only activate leave if mouse cursor is outside... bounding box..
25819         
25820         
25821         
25822         
25823         if (this.currentTip) {
25824             this.currentTip.leave();
25825         }
25826         //Roo.log('clear currentEl');
25827         this.currentEl = false;
25828         
25829         
25830     },
25831     alignment : {
25832         'left' : ['r-l', [-2,0], 'right'],
25833         'right' : ['l-r', [2,0], 'left'],
25834         'bottom' : ['t-b', [0,2], 'top'],
25835         'top' : [ 'b-t', [0,-2], 'bottom']
25836     }
25837     
25838 });
25839
25840
25841 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25842     
25843     
25844     bindEl : false,
25845     
25846     delay : null, // can be { show : 300 , hide: 500}
25847     
25848     timeout : null,
25849     
25850     hoverState : null, //???
25851     
25852     placement : 'bottom', 
25853     
25854     alignment : false,
25855     
25856     getAutoCreate : function(){
25857     
25858         var cfg = {
25859            cls : 'tooltip',
25860            role : 'tooltip',
25861            cn : [
25862                 {
25863                     cls : 'tooltip-arrow'
25864                 },
25865                 {
25866                     cls : 'tooltip-inner'
25867                 }
25868            ]
25869         };
25870         
25871         return cfg;
25872     },
25873     bind : function(el)
25874     {
25875         this.bindEl = el;
25876     },
25877       
25878     
25879     enter : function () {
25880        
25881         if (this.timeout != null) {
25882             clearTimeout(this.timeout);
25883         }
25884         
25885         this.hoverState = 'in';
25886          //Roo.log("enter - show");
25887         if (!this.delay || !this.delay.show) {
25888             this.show();
25889             return;
25890         }
25891         var _t = this;
25892         this.timeout = setTimeout(function () {
25893             if (_t.hoverState == 'in') {
25894                 _t.show();
25895             }
25896         }, this.delay.show);
25897     },
25898     leave : function()
25899     {
25900         clearTimeout(this.timeout);
25901     
25902         this.hoverState = 'out';
25903          if (!this.delay || !this.delay.hide) {
25904             this.hide();
25905             return;
25906         }
25907        
25908         var _t = this;
25909         this.timeout = setTimeout(function () {
25910             //Roo.log("leave - timeout");
25911             
25912             if (_t.hoverState == 'out') {
25913                 _t.hide();
25914                 Roo.bootstrap.Tooltip.currentEl = false;
25915             }
25916         }, delay);
25917     },
25918     
25919     show : function (msg)
25920     {
25921         if (!this.el) {
25922             this.render(document.body);
25923         }
25924         // set content.
25925         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25926         
25927         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25928         
25929         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25930         
25931         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25932         
25933         var placement = typeof this.placement == 'function' ?
25934             this.placement.call(this, this.el, on_el) :
25935             this.placement;
25936             
25937         var autoToken = /\s?auto?\s?/i;
25938         var autoPlace = autoToken.test(placement);
25939         if (autoPlace) {
25940             placement = placement.replace(autoToken, '') || 'top';
25941         }
25942         
25943         //this.el.detach()
25944         //this.el.setXY([0,0]);
25945         this.el.show();
25946         //this.el.dom.style.display='block';
25947         
25948         //this.el.appendTo(on_el);
25949         
25950         var p = this.getPosition();
25951         var box = this.el.getBox();
25952         
25953         if (autoPlace) {
25954             // fixme..
25955         }
25956         
25957         var align = this.alignment[placement];
25958         
25959         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25960         
25961         if(placement == 'top' || placement == 'bottom'){
25962             if(xy[0] < 0){
25963                 placement = 'right';
25964             }
25965             
25966             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25967                 placement = 'left';
25968             }
25969             
25970             var scroll = Roo.select('body', true).first().getScroll();
25971             
25972             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25973                 placement = 'top';
25974             }
25975             
25976         }
25977         
25978         this.el.alignTo(this.bindEl, align[0],align[1]);
25979         //var arrow = this.el.select('.arrow',true).first();
25980         //arrow.set(align[2], 
25981         
25982         this.el.addClass(placement);
25983         
25984         this.el.addClass('in fade');
25985         
25986         this.hoverState = null;
25987         
25988         if (this.el.hasClass('fade')) {
25989             // fade it?
25990         }
25991         
25992     },
25993     hide : function()
25994     {
25995          
25996         if (!this.el) {
25997             return;
25998         }
25999         //this.el.setXY([0,0]);
26000         this.el.removeClass('in');
26001         //this.el.hide();
26002         
26003     }
26004     
26005 });
26006  
26007
26008  /*
26009  * - LGPL
26010  *
26011  * Location Picker
26012  * 
26013  */
26014
26015 /**
26016  * @class Roo.bootstrap.LocationPicker
26017  * @extends Roo.bootstrap.Component
26018  * Bootstrap LocationPicker class
26019  * @cfg {Number} latitude Position when init default 0
26020  * @cfg {Number} longitude Position when init default 0
26021  * @cfg {Number} zoom default 15
26022  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26023  * @cfg {Boolean} mapTypeControl default false
26024  * @cfg {Boolean} disableDoubleClickZoom default false
26025  * @cfg {Boolean} scrollwheel default true
26026  * @cfg {Boolean} streetViewControl default false
26027  * @cfg {Number} radius default 0
26028  * @cfg {String} locationName
26029  * @cfg {Boolean} draggable default true
26030  * @cfg {Boolean} enableAutocomplete default false
26031  * @cfg {Boolean} enableReverseGeocode default true
26032  * @cfg {String} markerTitle
26033  * 
26034  * @constructor
26035  * Create a new LocationPicker
26036  * @param {Object} config The config object
26037  */
26038
26039
26040 Roo.bootstrap.LocationPicker = function(config){
26041     
26042     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26043     
26044     this.addEvents({
26045         /**
26046          * @event initial
26047          * Fires when the picker initialized.
26048          * @param {Roo.bootstrap.LocationPicker} this
26049          * @param {Google Location} location
26050          */
26051         initial : true,
26052         /**
26053          * @event positionchanged
26054          * Fires when the picker position changed.
26055          * @param {Roo.bootstrap.LocationPicker} this
26056          * @param {Google Location} location
26057          */
26058         positionchanged : true,
26059         /**
26060          * @event resize
26061          * Fires when the map resize.
26062          * @param {Roo.bootstrap.LocationPicker} this
26063          */
26064         resize : true,
26065         /**
26066          * @event show
26067          * Fires when the map show.
26068          * @param {Roo.bootstrap.LocationPicker} this
26069          */
26070         show : true,
26071         /**
26072          * @event hide
26073          * Fires when the map hide.
26074          * @param {Roo.bootstrap.LocationPicker} this
26075          */
26076         hide : true,
26077         /**
26078          * @event mapClick
26079          * Fires when click the map.
26080          * @param {Roo.bootstrap.LocationPicker} this
26081          * @param {Map event} e
26082          */
26083         mapClick : true,
26084         /**
26085          * @event mapRightClick
26086          * Fires when right click the map.
26087          * @param {Roo.bootstrap.LocationPicker} this
26088          * @param {Map event} e
26089          */
26090         mapRightClick : true,
26091         /**
26092          * @event markerClick
26093          * Fires when click the marker.
26094          * @param {Roo.bootstrap.LocationPicker} this
26095          * @param {Map event} e
26096          */
26097         markerClick : true,
26098         /**
26099          * @event markerRightClick
26100          * Fires when right click the marker.
26101          * @param {Roo.bootstrap.LocationPicker} this
26102          * @param {Map event} e
26103          */
26104         markerRightClick : true,
26105         /**
26106          * @event OverlayViewDraw
26107          * Fires when OverlayView Draw
26108          * @param {Roo.bootstrap.LocationPicker} this
26109          */
26110         OverlayViewDraw : true,
26111         /**
26112          * @event OverlayViewOnAdd
26113          * Fires when OverlayView Draw
26114          * @param {Roo.bootstrap.LocationPicker} this
26115          */
26116         OverlayViewOnAdd : true,
26117         /**
26118          * @event OverlayViewOnRemove
26119          * Fires when OverlayView Draw
26120          * @param {Roo.bootstrap.LocationPicker} this
26121          */
26122         OverlayViewOnRemove : true,
26123         /**
26124          * @event OverlayViewShow
26125          * Fires when OverlayView Draw
26126          * @param {Roo.bootstrap.LocationPicker} this
26127          * @param {Pixel} cpx
26128          */
26129         OverlayViewShow : true,
26130         /**
26131          * @event OverlayViewHide
26132          * Fires when OverlayView Draw
26133          * @param {Roo.bootstrap.LocationPicker} this
26134          */
26135         OverlayViewHide : true,
26136         /**
26137          * @event loadexception
26138          * Fires when load google lib failed.
26139          * @param {Roo.bootstrap.LocationPicker} this
26140          */
26141         loadexception : true
26142     });
26143         
26144 };
26145
26146 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26147     
26148     gMapContext: false,
26149     
26150     latitude: 0,
26151     longitude: 0,
26152     zoom: 15,
26153     mapTypeId: false,
26154     mapTypeControl: false,
26155     disableDoubleClickZoom: false,
26156     scrollwheel: true,
26157     streetViewControl: false,
26158     radius: 0,
26159     locationName: '',
26160     draggable: true,
26161     enableAutocomplete: false,
26162     enableReverseGeocode: true,
26163     markerTitle: '',
26164     
26165     getAutoCreate: function()
26166     {
26167
26168         var cfg = {
26169             tag: 'div',
26170             cls: 'roo-location-picker'
26171         };
26172         
26173         return cfg
26174     },
26175     
26176     initEvents: function(ct, position)
26177     {       
26178         if(!this.el.getWidth() || this.isApplied()){
26179             return;
26180         }
26181         
26182         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26183         
26184         this.initial();
26185     },
26186     
26187     initial: function()
26188     {
26189         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26190             this.fireEvent('loadexception', this);
26191             return;
26192         }
26193         
26194         if(!this.mapTypeId){
26195             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26196         }
26197         
26198         this.gMapContext = this.GMapContext();
26199         
26200         this.initOverlayView();
26201         
26202         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26203         
26204         var _this = this;
26205                 
26206         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26207             _this.setPosition(_this.gMapContext.marker.position);
26208         });
26209         
26210         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26211             _this.fireEvent('mapClick', this, event);
26212             
26213         });
26214
26215         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26216             _this.fireEvent('mapRightClick', this, event);
26217             
26218         });
26219         
26220         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26221             _this.fireEvent('markerClick', this, event);
26222             
26223         });
26224
26225         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26226             _this.fireEvent('markerRightClick', this, event);
26227             
26228         });
26229         
26230         this.setPosition(this.gMapContext.location);
26231         
26232         this.fireEvent('initial', this, this.gMapContext.location);
26233     },
26234     
26235     initOverlayView: function()
26236     {
26237         var _this = this;
26238         
26239         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26240             
26241             draw: function()
26242             {
26243                 _this.fireEvent('OverlayViewDraw', _this);
26244             },
26245             
26246             onAdd: function()
26247             {
26248                 _this.fireEvent('OverlayViewOnAdd', _this);
26249             },
26250             
26251             onRemove: function()
26252             {
26253                 _this.fireEvent('OverlayViewOnRemove', _this);
26254             },
26255             
26256             show: function(cpx)
26257             {
26258                 _this.fireEvent('OverlayViewShow', _this, cpx);
26259             },
26260             
26261             hide: function()
26262             {
26263                 _this.fireEvent('OverlayViewHide', _this);
26264             }
26265             
26266         });
26267     },
26268     
26269     fromLatLngToContainerPixel: function(event)
26270     {
26271         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26272     },
26273     
26274     isApplied: function() 
26275     {
26276         return this.getGmapContext() == false ? false : true;
26277     },
26278     
26279     getGmapContext: function() 
26280     {
26281         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26282     },
26283     
26284     GMapContext: function() 
26285     {
26286         var position = new google.maps.LatLng(this.latitude, this.longitude);
26287         
26288         var _map = new google.maps.Map(this.el.dom, {
26289             center: position,
26290             zoom: this.zoom,
26291             mapTypeId: this.mapTypeId,
26292             mapTypeControl: this.mapTypeControl,
26293             disableDoubleClickZoom: this.disableDoubleClickZoom,
26294             scrollwheel: this.scrollwheel,
26295             streetViewControl: this.streetViewControl,
26296             locationName: this.locationName,
26297             draggable: this.draggable,
26298             enableAutocomplete: this.enableAutocomplete,
26299             enableReverseGeocode: this.enableReverseGeocode
26300         });
26301         
26302         var _marker = new google.maps.Marker({
26303             position: position,
26304             map: _map,
26305             title: this.markerTitle,
26306             draggable: this.draggable
26307         });
26308         
26309         return {
26310             map: _map,
26311             marker: _marker,
26312             circle: null,
26313             location: position,
26314             radius: this.radius,
26315             locationName: this.locationName,
26316             addressComponents: {
26317                 formatted_address: null,
26318                 addressLine1: null,
26319                 addressLine2: null,
26320                 streetName: null,
26321                 streetNumber: null,
26322                 city: null,
26323                 district: null,
26324                 state: null,
26325                 stateOrProvince: null
26326             },
26327             settings: this,
26328             domContainer: this.el.dom,
26329             geodecoder: new google.maps.Geocoder()
26330         };
26331     },
26332     
26333     drawCircle: function(center, radius, options) 
26334     {
26335         if (this.gMapContext.circle != null) {
26336             this.gMapContext.circle.setMap(null);
26337         }
26338         if (radius > 0) {
26339             radius *= 1;
26340             options = Roo.apply({}, options, {
26341                 strokeColor: "#0000FF",
26342                 strokeOpacity: .35,
26343                 strokeWeight: 2,
26344                 fillColor: "#0000FF",
26345                 fillOpacity: .2
26346             });
26347             
26348             options.map = this.gMapContext.map;
26349             options.radius = radius;
26350             options.center = center;
26351             this.gMapContext.circle = new google.maps.Circle(options);
26352             return this.gMapContext.circle;
26353         }
26354         
26355         return null;
26356     },
26357     
26358     setPosition: function(location) 
26359     {
26360         this.gMapContext.location = location;
26361         this.gMapContext.marker.setPosition(location);
26362         this.gMapContext.map.panTo(location);
26363         this.drawCircle(location, this.gMapContext.radius, {});
26364         
26365         var _this = this;
26366         
26367         if (this.gMapContext.settings.enableReverseGeocode) {
26368             this.gMapContext.geodecoder.geocode({
26369                 latLng: this.gMapContext.location
26370             }, function(results, status) {
26371                 
26372                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26373                     _this.gMapContext.locationName = results[0].formatted_address;
26374                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26375                     
26376                     _this.fireEvent('positionchanged', this, location);
26377                 }
26378             });
26379             
26380             return;
26381         }
26382         
26383         this.fireEvent('positionchanged', this, location);
26384     },
26385     
26386     resize: function()
26387     {
26388         google.maps.event.trigger(this.gMapContext.map, "resize");
26389         
26390         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26391         
26392         this.fireEvent('resize', this);
26393     },
26394     
26395     setPositionByLatLng: function(latitude, longitude)
26396     {
26397         this.setPosition(new google.maps.LatLng(latitude, longitude));
26398     },
26399     
26400     getCurrentPosition: function() 
26401     {
26402         return {
26403             latitude: this.gMapContext.location.lat(),
26404             longitude: this.gMapContext.location.lng()
26405         };
26406     },
26407     
26408     getAddressName: function() 
26409     {
26410         return this.gMapContext.locationName;
26411     },
26412     
26413     getAddressComponents: function() 
26414     {
26415         return this.gMapContext.addressComponents;
26416     },
26417     
26418     address_component_from_google_geocode: function(address_components) 
26419     {
26420         var result = {};
26421         
26422         for (var i = 0; i < address_components.length; i++) {
26423             var component = address_components[i];
26424             if (component.types.indexOf("postal_code") >= 0) {
26425                 result.postalCode = component.short_name;
26426             } else if (component.types.indexOf("street_number") >= 0) {
26427                 result.streetNumber = component.short_name;
26428             } else if (component.types.indexOf("route") >= 0) {
26429                 result.streetName = component.short_name;
26430             } else if (component.types.indexOf("neighborhood") >= 0) {
26431                 result.city = component.short_name;
26432             } else if (component.types.indexOf("locality") >= 0) {
26433                 result.city = component.short_name;
26434             } else if (component.types.indexOf("sublocality") >= 0) {
26435                 result.district = component.short_name;
26436             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26437                 result.stateOrProvince = component.short_name;
26438             } else if (component.types.indexOf("country") >= 0) {
26439                 result.country = component.short_name;
26440             }
26441         }
26442         
26443         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26444         result.addressLine2 = "";
26445         return result;
26446     },
26447     
26448     setZoomLevel: function(zoom)
26449     {
26450         this.gMapContext.map.setZoom(zoom);
26451     },
26452     
26453     show: function()
26454     {
26455         if(!this.el){
26456             return;
26457         }
26458         
26459         this.el.show();
26460         
26461         this.resize();
26462         
26463         this.fireEvent('show', this);
26464     },
26465     
26466     hide: function()
26467     {
26468         if(!this.el){
26469             return;
26470         }
26471         
26472         this.el.hide();
26473         
26474         this.fireEvent('hide', this);
26475     }
26476     
26477 });
26478
26479 Roo.apply(Roo.bootstrap.LocationPicker, {
26480     
26481     OverlayView : function(map, options)
26482     {
26483         options = options || {};
26484         
26485         this.setMap(map);
26486     }
26487     
26488     
26489 });/*
26490  * - LGPL
26491  *
26492  * Alert
26493  * 
26494  */
26495
26496 /**
26497  * @class Roo.bootstrap.Alert
26498  * @extends Roo.bootstrap.Component
26499  * Bootstrap Alert class
26500  * @cfg {String} title The title of alert
26501  * @cfg {String} html The content of alert
26502  * @cfg {String} weight (  success | info | warning | danger )
26503  * @cfg {String} faicon font-awesomeicon
26504  * 
26505  * @constructor
26506  * Create a new alert
26507  * @param {Object} config The config object
26508  */
26509
26510
26511 Roo.bootstrap.Alert = function(config){
26512     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26513     
26514 };
26515
26516 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26517     
26518     title: '',
26519     html: '',
26520     weight: false,
26521     faicon: false,
26522     
26523     getAutoCreate : function()
26524     {
26525         
26526         var cfg = {
26527             tag : 'div',
26528             cls : 'alert',
26529             cn : [
26530                 {
26531                     tag : 'i',
26532                     cls : 'roo-alert-icon'
26533                     
26534                 },
26535                 {
26536                     tag : 'b',
26537                     cls : 'roo-alert-title',
26538                     html : this.title
26539                 },
26540                 {
26541                     tag : 'span',
26542                     cls : 'roo-alert-text',
26543                     html : this.html
26544                 }
26545             ]
26546         };
26547         
26548         if(this.faicon){
26549             cfg.cn[0].cls += ' fa ' + this.faicon;
26550         }
26551         
26552         if(this.weight){
26553             cfg.cls += ' alert-' + this.weight;
26554         }
26555         
26556         return cfg;
26557     },
26558     
26559     initEvents: function() 
26560     {
26561         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26562     },
26563     
26564     setTitle : function(str)
26565     {
26566         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26567     },
26568     
26569     setText : function(str)
26570     {
26571         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26572     },
26573     
26574     setWeight : function(weight)
26575     {
26576         if(this.weight){
26577             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26578         }
26579         
26580         this.weight = weight;
26581         
26582         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26583     },
26584     
26585     setIcon : function(icon)
26586     {
26587         if(this.faicon){
26588             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26589         }
26590         
26591         this.faicon = icon;
26592         
26593         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26594     },
26595     
26596     hide: function() 
26597     {
26598         this.el.hide();   
26599     },
26600     
26601     show: function() 
26602     {  
26603         this.el.show();   
26604     }
26605     
26606 });
26607
26608  
26609 /*
26610 * Licence: LGPL
26611 */
26612
26613 /**
26614  * @class Roo.bootstrap.UploadCropbox
26615  * @extends Roo.bootstrap.Component
26616  * Bootstrap UploadCropbox class
26617  * @cfg {String} emptyText show when image has been loaded
26618  * @cfg {String} rotateNotify show when image too small to rotate
26619  * @cfg {Number} errorTimeout default 3000
26620  * @cfg {Number} minWidth default 300
26621  * @cfg {Number} minHeight default 300
26622  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26623  * @cfg {Boolean} isDocument (true|false) default false
26624  * @cfg {String} url action url
26625  * @cfg {String} paramName default 'imageUpload'
26626  * @cfg {String} method default POST
26627  * @cfg {Boolean} loadMask (true|false) default true
26628  * @cfg {Boolean} loadingText default 'Loading...'
26629  * 
26630  * @constructor
26631  * Create a new UploadCropbox
26632  * @param {Object} config The config object
26633  */
26634
26635 Roo.bootstrap.UploadCropbox = function(config){
26636     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26637     
26638     this.addEvents({
26639         /**
26640          * @event beforeselectfile
26641          * Fire before select file
26642          * @param {Roo.bootstrap.UploadCropbox} this
26643          */
26644         "beforeselectfile" : true,
26645         /**
26646          * @event initial
26647          * Fire after initEvent
26648          * @param {Roo.bootstrap.UploadCropbox} this
26649          */
26650         "initial" : true,
26651         /**
26652          * @event crop
26653          * Fire after initEvent
26654          * @param {Roo.bootstrap.UploadCropbox} this
26655          * @param {String} data
26656          */
26657         "crop" : true,
26658         /**
26659          * @event prepare
26660          * Fire when preparing the file data
26661          * @param {Roo.bootstrap.UploadCropbox} this
26662          * @param {Object} file
26663          */
26664         "prepare" : true,
26665         /**
26666          * @event exception
26667          * Fire when get exception
26668          * @param {Roo.bootstrap.UploadCropbox} this
26669          * @param {XMLHttpRequest} xhr
26670          */
26671         "exception" : true,
26672         /**
26673          * @event beforeloadcanvas
26674          * Fire before load the canvas
26675          * @param {Roo.bootstrap.UploadCropbox} this
26676          * @param {String} src
26677          */
26678         "beforeloadcanvas" : true,
26679         /**
26680          * @event trash
26681          * Fire when trash image
26682          * @param {Roo.bootstrap.UploadCropbox} this
26683          */
26684         "trash" : true,
26685         /**
26686          * @event download
26687          * Fire when download the image
26688          * @param {Roo.bootstrap.UploadCropbox} this
26689          */
26690         "download" : true,
26691         /**
26692          * @event footerbuttonclick
26693          * Fire when footerbuttonclick
26694          * @param {Roo.bootstrap.UploadCropbox} this
26695          * @param {String} type
26696          */
26697         "footerbuttonclick" : true,
26698         /**
26699          * @event resize
26700          * Fire when resize
26701          * @param {Roo.bootstrap.UploadCropbox} this
26702          */
26703         "resize" : true,
26704         /**
26705          * @event rotate
26706          * Fire when rotate the image
26707          * @param {Roo.bootstrap.UploadCropbox} this
26708          * @param {String} pos
26709          */
26710         "rotate" : true,
26711         /**
26712          * @event inspect
26713          * Fire when inspect the file
26714          * @param {Roo.bootstrap.UploadCropbox} this
26715          * @param {Object} file
26716          */
26717         "inspect" : true,
26718         /**
26719          * @event upload
26720          * Fire when xhr upload the file
26721          * @param {Roo.bootstrap.UploadCropbox} this
26722          * @param {Object} data
26723          */
26724         "upload" : true,
26725         /**
26726          * @event arrange
26727          * Fire when arrange the file data
26728          * @param {Roo.bootstrap.UploadCropbox} this
26729          * @param {Object} formData
26730          */
26731         "arrange" : true
26732     });
26733     
26734     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26735 };
26736
26737 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26738     
26739     emptyText : 'Click to upload image',
26740     rotateNotify : 'Image is too small to rotate',
26741     errorTimeout : 3000,
26742     scale : 0,
26743     baseScale : 1,
26744     rotate : 0,
26745     dragable : false,
26746     pinching : false,
26747     mouseX : 0,
26748     mouseY : 0,
26749     cropData : false,
26750     minWidth : 300,
26751     minHeight : 300,
26752     file : false,
26753     exif : {},
26754     baseRotate : 1,
26755     cropType : 'image/jpeg',
26756     buttons : false,
26757     canvasLoaded : false,
26758     isDocument : false,
26759     method : 'POST',
26760     paramName : 'imageUpload',
26761     loadMask : true,
26762     loadingText : 'Loading...',
26763     maskEl : false,
26764     
26765     getAutoCreate : function()
26766     {
26767         var cfg = {
26768             tag : 'div',
26769             cls : 'roo-upload-cropbox',
26770             cn : [
26771                 {
26772                     tag : 'input',
26773                     cls : 'roo-upload-cropbox-selector',
26774                     type : 'file'
26775                 },
26776                 {
26777                     tag : 'div',
26778                     cls : 'roo-upload-cropbox-body',
26779                     style : 'cursor:pointer',
26780                     cn : [
26781                         {
26782                             tag : 'div',
26783                             cls : 'roo-upload-cropbox-preview'
26784                         },
26785                         {
26786                             tag : 'div',
26787                             cls : 'roo-upload-cropbox-thumb'
26788                         },
26789                         {
26790                             tag : 'div',
26791                             cls : 'roo-upload-cropbox-empty-notify',
26792                             html : this.emptyText
26793                         },
26794                         {
26795                             tag : 'div',
26796                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26797                             html : this.rotateNotify
26798                         }
26799                     ]
26800                 },
26801                 {
26802                     tag : 'div',
26803                     cls : 'roo-upload-cropbox-footer',
26804                     cn : {
26805                         tag : 'div',
26806                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26807                         cn : []
26808                     }
26809                 }
26810             ]
26811         };
26812         
26813         return cfg;
26814     },
26815     
26816     onRender : function(ct, position)
26817     {
26818         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26819         
26820         if (this.buttons.length) {
26821             
26822             Roo.each(this.buttons, function(bb) {
26823                 
26824                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26825                 
26826                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26827                 
26828             }, this);
26829         }
26830         
26831         if(this.loadMask){
26832             this.maskEl = this.el;
26833         }
26834     },
26835     
26836     initEvents : function()
26837     {
26838         this.urlAPI = (window.createObjectURL && window) || 
26839                                 (window.URL && URL.revokeObjectURL && URL) || 
26840                                 (window.webkitURL && webkitURL);
26841                         
26842         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26843         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26844         
26845         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26846         this.selectorEl.hide();
26847         
26848         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26849         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26850         
26851         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26852         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26853         this.thumbEl.hide();
26854         
26855         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26856         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26857         
26858         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26859         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26860         this.errorEl.hide();
26861         
26862         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26863         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26864         this.footerEl.hide();
26865         
26866         this.setThumbBoxSize();
26867         
26868         this.bind();
26869         
26870         this.resize();
26871         
26872         this.fireEvent('initial', this);
26873     },
26874
26875     bind : function()
26876     {
26877         var _this = this;
26878         
26879         window.addEventListener("resize", function() { _this.resize(); } );
26880         
26881         this.bodyEl.on('click', this.beforeSelectFile, this);
26882         
26883         if(Roo.isTouch){
26884             this.bodyEl.on('touchstart', this.onTouchStart, this);
26885             this.bodyEl.on('touchmove', this.onTouchMove, this);
26886             this.bodyEl.on('touchend', this.onTouchEnd, this);
26887         }
26888         
26889         if(!Roo.isTouch){
26890             this.bodyEl.on('mousedown', this.onMouseDown, this);
26891             this.bodyEl.on('mousemove', this.onMouseMove, this);
26892             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26893             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26894             Roo.get(document).on('mouseup', this.onMouseUp, this);
26895         }
26896         
26897         this.selectorEl.on('change', this.onFileSelected, this);
26898     },
26899     
26900     reset : function()
26901     {    
26902         this.scale = 0;
26903         this.baseScale = 1;
26904         this.rotate = 0;
26905         this.baseRotate = 1;
26906         this.dragable = false;
26907         this.pinching = false;
26908         this.mouseX = 0;
26909         this.mouseY = 0;
26910         this.cropData = false;
26911         this.notifyEl.dom.innerHTML = this.emptyText;
26912         
26913         this.selectorEl.dom.value = '';
26914         
26915     },
26916     
26917     resize : function()
26918     {
26919         if(this.fireEvent('resize', this) != false){
26920             this.setThumbBoxPosition();
26921             this.setCanvasPosition();
26922         }
26923     },
26924     
26925     onFooterButtonClick : function(e, el, o, type)
26926     {
26927         switch (type) {
26928             case 'rotate-left' :
26929                 this.onRotateLeft(e);
26930                 break;
26931             case 'rotate-right' :
26932                 this.onRotateRight(e);
26933                 break;
26934             case 'picture' :
26935                 this.beforeSelectFile(e);
26936                 break;
26937             case 'trash' :
26938                 this.trash(e);
26939                 break;
26940             case 'crop' :
26941                 this.crop(e);
26942                 break;
26943             case 'download' :
26944                 this.download(e);
26945                 break;
26946             default :
26947                 break;
26948         }
26949         
26950         this.fireEvent('footerbuttonclick', this, type);
26951     },
26952     
26953     beforeSelectFile : function(e)
26954     {
26955         e.preventDefault();
26956         
26957         if(this.fireEvent('beforeselectfile', this) != false){
26958             this.selectorEl.dom.click();
26959         }
26960     },
26961     
26962     onFileSelected : function(e)
26963     {
26964         e.preventDefault();
26965         
26966         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26967             return;
26968         }
26969         
26970         var file = this.selectorEl.dom.files[0];
26971         
26972         if(this.fireEvent('inspect', this, file) != false){
26973             this.prepare(file);
26974         }
26975         
26976     },
26977     
26978     trash : function(e)
26979     {
26980         this.fireEvent('trash', this);
26981     },
26982     
26983     download : function(e)
26984     {
26985         this.fireEvent('download', this);
26986     },
26987     
26988     loadCanvas : function(src)
26989     {   
26990         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26991             
26992             this.reset();
26993             
26994             this.imageEl = document.createElement('img');
26995             
26996             var _this = this;
26997             
26998             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26999             
27000             this.imageEl.src = src;
27001         }
27002     },
27003     
27004     onLoadCanvas : function()
27005     {   
27006         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27007         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27008         
27009         this.bodyEl.un('click', this.beforeSelectFile, this);
27010         
27011         this.notifyEl.hide();
27012         this.thumbEl.show();
27013         this.footerEl.show();
27014         
27015         this.baseRotateLevel();
27016         
27017         if(this.isDocument){
27018             this.setThumbBoxSize();
27019         }
27020         
27021         this.setThumbBoxPosition();
27022         
27023         this.baseScaleLevel();
27024         
27025         this.draw();
27026         
27027         this.resize();
27028         
27029         this.canvasLoaded = true;
27030         
27031         if(this.loadMask){
27032             this.maskEl.unmask();
27033         }
27034         
27035     },
27036     
27037     setCanvasPosition : function()
27038     {   
27039         if(!this.canvasEl){
27040             return;
27041         }
27042         
27043         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27044         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27045         
27046         this.previewEl.setLeft(pw);
27047         this.previewEl.setTop(ph);
27048         
27049     },
27050     
27051     onMouseDown : function(e)
27052     {   
27053         e.stopEvent();
27054         
27055         this.dragable = true;
27056         this.pinching = false;
27057         
27058         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27059             this.dragable = false;
27060             return;
27061         }
27062         
27063         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27064         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27065         
27066     },
27067     
27068     onMouseMove : function(e)
27069     {   
27070         e.stopEvent();
27071         
27072         if(!this.canvasLoaded){
27073             return;
27074         }
27075         
27076         if (!this.dragable){
27077             return;
27078         }
27079         
27080         var minX = Math.ceil(this.thumbEl.getLeft(true));
27081         var minY = Math.ceil(this.thumbEl.getTop(true));
27082         
27083         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27084         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27085         
27086         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27087         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27088         
27089         x = x - this.mouseX;
27090         y = y - this.mouseY;
27091         
27092         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27093         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27094         
27095         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27096         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27097         
27098         this.previewEl.setLeft(bgX);
27099         this.previewEl.setTop(bgY);
27100         
27101         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27102         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27103     },
27104     
27105     onMouseUp : function(e)
27106     {   
27107         e.stopEvent();
27108         
27109         this.dragable = false;
27110     },
27111     
27112     onMouseWheel : function(e)
27113     {   
27114         e.stopEvent();
27115         
27116         this.startScale = this.scale;
27117         
27118         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27119         
27120         if(!this.zoomable()){
27121             this.scale = this.startScale;
27122             return;
27123         }
27124         
27125         this.draw();
27126         
27127         return;
27128     },
27129     
27130     zoomable : function()
27131     {
27132         var minScale = this.thumbEl.getWidth() / this.minWidth;
27133         
27134         if(this.minWidth < this.minHeight){
27135             minScale = this.thumbEl.getHeight() / this.minHeight;
27136         }
27137         
27138         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27139         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27140         
27141         if(
27142                 this.isDocument &&
27143                 (this.rotate == 0 || this.rotate == 180) && 
27144                 (
27145                     width > this.imageEl.OriginWidth || 
27146                     height > this.imageEl.OriginHeight ||
27147                     (width < this.minWidth && height < this.minHeight)
27148                 )
27149         ){
27150             return false;
27151         }
27152         
27153         if(
27154                 this.isDocument &&
27155                 (this.rotate == 90 || this.rotate == 270) && 
27156                 (
27157                     width > this.imageEl.OriginWidth || 
27158                     height > this.imageEl.OriginHeight ||
27159                     (width < this.minHeight && height < this.minWidth)
27160                 )
27161         ){
27162             return false;
27163         }
27164         
27165         if(
27166                 !this.isDocument &&
27167                 (this.rotate == 0 || this.rotate == 180) && 
27168                 (
27169                     width < this.minWidth || 
27170                     width > this.imageEl.OriginWidth || 
27171                     height < this.minHeight || 
27172                     height > this.imageEl.OriginHeight
27173                 )
27174         ){
27175             return false;
27176         }
27177         
27178         if(
27179                 !this.isDocument &&
27180                 (this.rotate == 90 || this.rotate == 270) && 
27181                 (
27182                     width < this.minHeight || 
27183                     width > this.imageEl.OriginWidth || 
27184                     height < this.minWidth || 
27185                     height > this.imageEl.OriginHeight
27186                 )
27187         ){
27188             return false;
27189         }
27190         
27191         return true;
27192         
27193     },
27194     
27195     onRotateLeft : function(e)
27196     {   
27197         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27198             
27199             var minScale = this.thumbEl.getWidth() / this.minWidth;
27200             
27201             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27202             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27203             
27204             this.startScale = this.scale;
27205             
27206             while (this.getScaleLevel() < minScale){
27207             
27208                 this.scale = this.scale + 1;
27209                 
27210                 if(!this.zoomable()){
27211                     break;
27212                 }
27213                 
27214                 if(
27215                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27216                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27217                 ){
27218                     continue;
27219                 }
27220                 
27221                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27222
27223                 this.draw();
27224                 
27225                 return;
27226             }
27227             
27228             this.scale = this.startScale;
27229             
27230             this.onRotateFail();
27231             
27232             return false;
27233         }
27234         
27235         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27236
27237         if(this.isDocument){
27238             this.setThumbBoxSize();
27239             this.setThumbBoxPosition();
27240             this.setCanvasPosition();
27241         }
27242         
27243         this.draw();
27244         
27245         this.fireEvent('rotate', this, 'left');
27246         
27247     },
27248     
27249     onRotateRight : function(e)
27250     {
27251         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27252             
27253             var minScale = this.thumbEl.getWidth() / this.minWidth;
27254         
27255             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27256             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27257             
27258             this.startScale = this.scale;
27259             
27260             while (this.getScaleLevel() < minScale){
27261             
27262                 this.scale = this.scale + 1;
27263                 
27264                 if(!this.zoomable()){
27265                     break;
27266                 }
27267                 
27268                 if(
27269                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27270                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27271                 ){
27272                     continue;
27273                 }
27274                 
27275                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27276
27277                 this.draw();
27278                 
27279                 return;
27280             }
27281             
27282             this.scale = this.startScale;
27283             
27284             this.onRotateFail();
27285             
27286             return false;
27287         }
27288         
27289         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27290
27291         if(this.isDocument){
27292             this.setThumbBoxSize();
27293             this.setThumbBoxPosition();
27294             this.setCanvasPosition();
27295         }
27296         
27297         this.draw();
27298         
27299         this.fireEvent('rotate', this, 'right');
27300     },
27301     
27302     onRotateFail : function()
27303     {
27304         this.errorEl.show(true);
27305         
27306         var _this = this;
27307         
27308         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27309     },
27310     
27311     draw : function()
27312     {
27313         this.previewEl.dom.innerHTML = '';
27314         
27315         var canvasEl = document.createElement("canvas");
27316         
27317         var contextEl = canvasEl.getContext("2d");
27318         
27319         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27320         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27321         var center = this.imageEl.OriginWidth / 2;
27322         
27323         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27324             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27325             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27326             center = this.imageEl.OriginHeight / 2;
27327         }
27328         
27329         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27330         
27331         contextEl.translate(center, center);
27332         contextEl.rotate(this.rotate * Math.PI / 180);
27333
27334         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27335         
27336         this.canvasEl = document.createElement("canvas");
27337         
27338         this.contextEl = this.canvasEl.getContext("2d");
27339         
27340         switch (this.rotate) {
27341             case 0 :
27342                 
27343                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27344                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27345                 
27346                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27347                 
27348                 break;
27349             case 90 : 
27350                 
27351                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27352                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27353                 
27354                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27355                     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);
27356                     break;
27357                 }
27358                 
27359                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27360                 
27361                 break;
27362             case 180 :
27363                 
27364                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27365                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27366                 
27367                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27368                     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);
27369                     break;
27370                 }
27371                 
27372                 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);
27373                 
27374                 break;
27375             case 270 :
27376                 
27377                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27378                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27379         
27380                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27381                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27382                     break;
27383                 }
27384                 
27385                 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);
27386                 
27387                 break;
27388             default : 
27389                 break;
27390         }
27391         
27392         this.previewEl.appendChild(this.canvasEl);
27393         
27394         this.setCanvasPosition();
27395     },
27396     
27397     crop : function()
27398     {
27399         if(!this.canvasLoaded){
27400             return;
27401         }
27402         
27403         var imageCanvas = document.createElement("canvas");
27404         
27405         var imageContext = imageCanvas.getContext("2d");
27406         
27407         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27408         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27409         
27410         var center = imageCanvas.width / 2;
27411         
27412         imageContext.translate(center, center);
27413         
27414         imageContext.rotate(this.rotate * Math.PI / 180);
27415         
27416         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27417         
27418         var canvas = document.createElement("canvas");
27419         
27420         var context = canvas.getContext("2d");
27421                 
27422         canvas.width = this.minWidth;
27423         canvas.height = this.minHeight;
27424
27425         switch (this.rotate) {
27426             case 0 :
27427                 
27428                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27429                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27430                 
27431                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27432                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27433                 
27434                 var targetWidth = this.minWidth - 2 * x;
27435                 var targetHeight = this.minHeight - 2 * y;
27436                 
27437                 var scale = 1;
27438                 
27439                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27440                     scale = targetWidth / width;
27441                 }
27442                 
27443                 if(x > 0 && y == 0){
27444                     scale = targetHeight / height;
27445                 }
27446                 
27447                 if(x > 0 && y > 0){
27448                     scale = targetWidth / width;
27449                     
27450                     if(width < height){
27451                         scale = targetHeight / height;
27452                     }
27453                 }
27454                 
27455                 context.scale(scale, scale);
27456                 
27457                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27458                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27459
27460                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27461                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27462
27463                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27464                 
27465                 break;
27466             case 90 : 
27467                 
27468                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27469                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27470                 
27471                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27472                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27473                 
27474                 var targetWidth = this.minWidth - 2 * x;
27475                 var targetHeight = this.minHeight - 2 * y;
27476                 
27477                 var scale = 1;
27478                 
27479                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27480                     scale = targetWidth / width;
27481                 }
27482                 
27483                 if(x > 0 && y == 0){
27484                     scale = targetHeight / height;
27485                 }
27486                 
27487                 if(x > 0 && y > 0){
27488                     scale = targetWidth / width;
27489                     
27490                     if(width < height){
27491                         scale = targetHeight / height;
27492                     }
27493                 }
27494                 
27495                 context.scale(scale, scale);
27496                 
27497                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27498                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27499
27500                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27501                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27502                 
27503                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27504                 
27505                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27506                 
27507                 break;
27508             case 180 :
27509                 
27510                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27511                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27512                 
27513                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27514                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27515                 
27516                 var targetWidth = this.minWidth - 2 * x;
27517                 var targetHeight = this.minHeight - 2 * y;
27518                 
27519                 var scale = 1;
27520                 
27521                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27522                     scale = targetWidth / width;
27523                 }
27524                 
27525                 if(x > 0 && y == 0){
27526                     scale = targetHeight / height;
27527                 }
27528                 
27529                 if(x > 0 && y > 0){
27530                     scale = targetWidth / width;
27531                     
27532                     if(width < height){
27533                         scale = targetHeight / height;
27534                     }
27535                 }
27536                 
27537                 context.scale(scale, scale);
27538                 
27539                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27540                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27541
27542                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27543                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27544
27545                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27546                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27547                 
27548                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27549                 
27550                 break;
27551             case 270 :
27552                 
27553                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27554                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27555                 
27556                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27557                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27558                 
27559                 var targetWidth = this.minWidth - 2 * x;
27560                 var targetHeight = this.minHeight - 2 * y;
27561                 
27562                 var scale = 1;
27563                 
27564                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27565                     scale = targetWidth / width;
27566                 }
27567                 
27568                 if(x > 0 && y == 0){
27569                     scale = targetHeight / height;
27570                 }
27571                 
27572                 if(x > 0 && y > 0){
27573                     scale = targetWidth / width;
27574                     
27575                     if(width < height){
27576                         scale = targetHeight / height;
27577                     }
27578                 }
27579                 
27580                 context.scale(scale, scale);
27581                 
27582                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27583                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27584
27585                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27586                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27587                 
27588                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27589                 
27590                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27591                 
27592                 break;
27593             default : 
27594                 break;
27595         }
27596         
27597         this.cropData = canvas.toDataURL(this.cropType);
27598         
27599         if(this.fireEvent('crop', this, this.cropData) !== false){
27600             this.process(this.file, this.cropData);
27601         }
27602         
27603         return;
27604         
27605     },
27606     
27607     setThumbBoxSize : function()
27608     {
27609         var width, height;
27610         
27611         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27612             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27613             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27614             
27615             this.minWidth = width;
27616             this.minHeight = height;
27617             
27618             if(this.rotate == 90 || this.rotate == 270){
27619                 this.minWidth = height;
27620                 this.minHeight = width;
27621             }
27622         }
27623         
27624         height = 300;
27625         width = Math.ceil(this.minWidth * height / this.minHeight);
27626         
27627         if(this.minWidth > this.minHeight){
27628             width = 300;
27629             height = Math.ceil(this.minHeight * width / this.minWidth);
27630         }
27631         
27632         this.thumbEl.setStyle({
27633             width : width + 'px',
27634             height : height + 'px'
27635         });
27636
27637         return;
27638             
27639     },
27640     
27641     setThumbBoxPosition : function()
27642     {
27643         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27644         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27645         
27646         this.thumbEl.setLeft(x);
27647         this.thumbEl.setTop(y);
27648         
27649     },
27650     
27651     baseRotateLevel : function()
27652     {
27653         this.baseRotate = 1;
27654         
27655         if(
27656                 typeof(this.exif) != 'undefined' &&
27657                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27658                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27659         ){
27660             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27661         }
27662         
27663         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27664         
27665     },
27666     
27667     baseScaleLevel : function()
27668     {
27669         var width, height;
27670         
27671         if(this.isDocument){
27672             
27673             if(this.baseRotate == 6 || this.baseRotate == 8){
27674             
27675                 height = this.thumbEl.getHeight();
27676                 this.baseScale = height / this.imageEl.OriginWidth;
27677
27678                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27679                     width = this.thumbEl.getWidth();
27680                     this.baseScale = width / this.imageEl.OriginHeight;
27681                 }
27682
27683                 return;
27684             }
27685
27686             height = this.thumbEl.getHeight();
27687             this.baseScale = height / this.imageEl.OriginHeight;
27688
27689             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27690                 width = this.thumbEl.getWidth();
27691                 this.baseScale = width / this.imageEl.OriginWidth;
27692             }
27693
27694             return;
27695         }
27696         
27697         if(this.baseRotate == 6 || this.baseRotate == 8){
27698             
27699             width = this.thumbEl.getHeight();
27700             this.baseScale = width / this.imageEl.OriginHeight;
27701             
27702             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27703                 height = this.thumbEl.getWidth();
27704                 this.baseScale = height / this.imageEl.OriginHeight;
27705             }
27706             
27707             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27708                 height = this.thumbEl.getWidth();
27709                 this.baseScale = height / this.imageEl.OriginHeight;
27710                 
27711                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27712                     width = this.thumbEl.getHeight();
27713                     this.baseScale = width / this.imageEl.OriginWidth;
27714                 }
27715             }
27716             
27717             return;
27718         }
27719         
27720         width = this.thumbEl.getWidth();
27721         this.baseScale = width / this.imageEl.OriginWidth;
27722         
27723         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27724             height = this.thumbEl.getHeight();
27725             this.baseScale = height / this.imageEl.OriginHeight;
27726         }
27727         
27728         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27729             
27730             height = this.thumbEl.getHeight();
27731             this.baseScale = height / this.imageEl.OriginHeight;
27732             
27733             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27734                 width = this.thumbEl.getWidth();
27735                 this.baseScale = width / this.imageEl.OriginWidth;
27736             }
27737             
27738         }
27739         
27740         return;
27741     },
27742     
27743     getScaleLevel : function()
27744     {
27745         return this.baseScale * Math.pow(1.1, this.scale);
27746     },
27747     
27748     onTouchStart : function(e)
27749     {
27750         if(!this.canvasLoaded){
27751             this.beforeSelectFile(e);
27752             return;
27753         }
27754         
27755         var touches = e.browserEvent.touches;
27756         
27757         if(!touches){
27758             return;
27759         }
27760         
27761         if(touches.length == 1){
27762             this.onMouseDown(e);
27763             return;
27764         }
27765         
27766         if(touches.length != 2){
27767             return;
27768         }
27769         
27770         var coords = [];
27771         
27772         for(var i = 0, finger; finger = touches[i]; i++){
27773             coords.push(finger.pageX, finger.pageY);
27774         }
27775         
27776         var x = Math.pow(coords[0] - coords[2], 2);
27777         var y = Math.pow(coords[1] - coords[3], 2);
27778         
27779         this.startDistance = Math.sqrt(x + y);
27780         
27781         this.startScale = this.scale;
27782         
27783         this.pinching = true;
27784         this.dragable = false;
27785         
27786     },
27787     
27788     onTouchMove : function(e)
27789     {
27790         if(!this.pinching && !this.dragable){
27791             return;
27792         }
27793         
27794         var touches = e.browserEvent.touches;
27795         
27796         if(!touches){
27797             return;
27798         }
27799         
27800         if(this.dragable){
27801             this.onMouseMove(e);
27802             return;
27803         }
27804         
27805         var coords = [];
27806         
27807         for(var i = 0, finger; finger = touches[i]; i++){
27808             coords.push(finger.pageX, finger.pageY);
27809         }
27810         
27811         var x = Math.pow(coords[0] - coords[2], 2);
27812         var y = Math.pow(coords[1] - coords[3], 2);
27813         
27814         this.endDistance = Math.sqrt(x + y);
27815         
27816         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27817         
27818         if(!this.zoomable()){
27819             this.scale = this.startScale;
27820             return;
27821         }
27822         
27823         this.draw();
27824         
27825     },
27826     
27827     onTouchEnd : function(e)
27828     {
27829         this.pinching = false;
27830         this.dragable = false;
27831         
27832     },
27833     
27834     process : function(file, crop)
27835     {
27836         if(this.loadMask){
27837             this.maskEl.mask(this.loadingText);
27838         }
27839         
27840         this.xhr = new XMLHttpRequest();
27841         
27842         file.xhr = this.xhr;
27843
27844         this.xhr.open(this.method, this.url, true);
27845         
27846         var headers = {
27847             "Accept": "application/json",
27848             "Cache-Control": "no-cache",
27849             "X-Requested-With": "XMLHttpRequest"
27850         };
27851         
27852         for (var headerName in headers) {
27853             var headerValue = headers[headerName];
27854             if (headerValue) {
27855                 this.xhr.setRequestHeader(headerName, headerValue);
27856             }
27857         }
27858         
27859         var _this = this;
27860         
27861         this.xhr.onload = function()
27862         {
27863             _this.xhrOnLoad(_this.xhr);
27864         }
27865         
27866         this.xhr.onerror = function()
27867         {
27868             _this.xhrOnError(_this.xhr);
27869         }
27870         
27871         var formData = new FormData();
27872
27873         formData.append('returnHTML', 'NO');
27874         
27875         if(crop){
27876             formData.append('crop', crop);
27877         }
27878         
27879         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27880             formData.append(this.paramName, file, file.name);
27881         }
27882         
27883         if(typeof(file.filename) != 'undefined'){
27884             formData.append('filename', file.filename);
27885         }
27886         
27887         if(typeof(file.mimetype) != 'undefined'){
27888             formData.append('mimetype', file.mimetype);
27889         }
27890         
27891         if(this.fireEvent('arrange', this, formData) != false){
27892             this.xhr.send(formData);
27893         };
27894     },
27895     
27896     xhrOnLoad : function(xhr)
27897     {
27898         if(this.loadMask){
27899             this.maskEl.unmask();
27900         }
27901         
27902         if (xhr.readyState !== 4) {
27903             this.fireEvent('exception', this, xhr);
27904             return;
27905         }
27906
27907         var response = Roo.decode(xhr.responseText);
27908         
27909         if(!response.success){
27910             this.fireEvent('exception', this, xhr);
27911             return;
27912         }
27913         
27914         var response = Roo.decode(xhr.responseText);
27915         
27916         this.fireEvent('upload', this, response);
27917         
27918     },
27919     
27920     xhrOnError : function()
27921     {
27922         if(this.loadMask){
27923             this.maskEl.unmask();
27924         }
27925         
27926         Roo.log('xhr on error');
27927         
27928         var response = Roo.decode(xhr.responseText);
27929           
27930         Roo.log(response);
27931         
27932     },
27933     
27934     prepare : function(file)
27935     {   
27936         if(this.loadMask){
27937             this.maskEl.mask(this.loadingText);
27938         }
27939         
27940         this.file = false;
27941         this.exif = {};
27942         
27943         if(typeof(file) === 'string'){
27944             this.loadCanvas(file);
27945             return;
27946         }
27947         
27948         if(!file || !this.urlAPI){
27949             return;
27950         }
27951         
27952         this.file = file;
27953         this.cropType = file.type;
27954         
27955         var _this = this;
27956         
27957         if(this.fireEvent('prepare', this, this.file) != false){
27958             
27959             var reader = new FileReader();
27960             
27961             reader.onload = function (e) {
27962                 if (e.target.error) {
27963                     Roo.log(e.target.error);
27964                     return;
27965                 }
27966                 
27967                 var buffer = e.target.result,
27968                     dataView = new DataView(buffer),
27969                     offset = 2,
27970                     maxOffset = dataView.byteLength - 4,
27971                     markerBytes,
27972                     markerLength;
27973                 
27974                 if (dataView.getUint16(0) === 0xffd8) {
27975                     while (offset < maxOffset) {
27976                         markerBytes = dataView.getUint16(offset);
27977                         
27978                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27979                             markerLength = dataView.getUint16(offset + 2) + 2;
27980                             if (offset + markerLength > dataView.byteLength) {
27981                                 Roo.log('Invalid meta data: Invalid segment size.');
27982                                 break;
27983                             }
27984                             
27985                             if(markerBytes == 0xffe1){
27986                                 _this.parseExifData(
27987                                     dataView,
27988                                     offset,
27989                                     markerLength
27990                                 );
27991                             }
27992                             
27993                             offset += markerLength;
27994                             
27995                             continue;
27996                         }
27997                         
27998                         break;
27999                     }
28000                     
28001                 }
28002                 
28003                 var url = _this.urlAPI.createObjectURL(_this.file);
28004                 
28005                 _this.loadCanvas(url);
28006                 
28007                 return;
28008             }
28009             
28010             reader.readAsArrayBuffer(this.file);
28011             
28012         }
28013         
28014     },
28015     
28016     parseExifData : function(dataView, offset, length)
28017     {
28018         var tiffOffset = offset + 10,
28019             littleEndian,
28020             dirOffset;
28021     
28022         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28023             // No Exif data, might be XMP data instead
28024             return;
28025         }
28026         
28027         // Check for the ASCII code for "Exif" (0x45786966):
28028         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28029             // No Exif data, might be XMP data instead
28030             return;
28031         }
28032         if (tiffOffset + 8 > dataView.byteLength) {
28033             Roo.log('Invalid Exif data: Invalid segment size.');
28034             return;
28035         }
28036         // Check for the two null bytes:
28037         if (dataView.getUint16(offset + 8) !== 0x0000) {
28038             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28039             return;
28040         }
28041         // Check the byte alignment:
28042         switch (dataView.getUint16(tiffOffset)) {
28043         case 0x4949:
28044             littleEndian = true;
28045             break;
28046         case 0x4D4D:
28047             littleEndian = false;
28048             break;
28049         default:
28050             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28051             return;
28052         }
28053         // Check for the TIFF tag marker (0x002A):
28054         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28055             Roo.log('Invalid Exif data: Missing TIFF marker.');
28056             return;
28057         }
28058         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28059         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28060         
28061         this.parseExifTags(
28062             dataView,
28063             tiffOffset,
28064             tiffOffset + dirOffset,
28065             littleEndian
28066         );
28067     },
28068     
28069     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28070     {
28071         var tagsNumber,
28072             dirEndOffset,
28073             i;
28074         if (dirOffset + 6 > dataView.byteLength) {
28075             Roo.log('Invalid Exif data: Invalid directory offset.');
28076             return;
28077         }
28078         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28079         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28080         if (dirEndOffset + 4 > dataView.byteLength) {
28081             Roo.log('Invalid Exif data: Invalid directory size.');
28082             return;
28083         }
28084         for (i = 0; i < tagsNumber; i += 1) {
28085             this.parseExifTag(
28086                 dataView,
28087                 tiffOffset,
28088                 dirOffset + 2 + 12 * i, // tag offset
28089                 littleEndian
28090             );
28091         }
28092         // Return the offset to the next directory:
28093         return dataView.getUint32(dirEndOffset, littleEndian);
28094     },
28095     
28096     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28097     {
28098         var tag = dataView.getUint16(offset, littleEndian);
28099         
28100         this.exif[tag] = this.getExifValue(
28101             dataView,
28102             tiffOffset,
28103             offset,
28104             dataView.getUint16(offset + 2, littleEndian), // tag type
28105             dataView.getUint32(offset + 4, littleEndian), // tag length
28106             littleEndian
28107         );
28108     },
28109     
28110     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28111     {
28112         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28113             tagSize,
28114             dataOffset,
28115             values,
28116             i,
28117             str,
28118             c;
28119     
28120         if (!tagType) {
28121             Roo.log('Invalid Exif data: Invalid tag type.');
28122             return;
28123         }
28124         
28125         tagSize = tagType.size * length;
28126         // Determine if the value is contained in the dataOffset bytes,
28127         // or if the value at the dataOffset is a pointer to the actual data:
28128         dataOffset = tagSize > 4 ?
28129                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28130         if (dataOffset + tagSize > dataView.byteLength) {
28131             Roo.log('Invalid Exif data: Invalid data offset.');
28132             return;
28133         }
28134         if (length === 1) {
28135             return tagType.getValue(dataView, dataOffset, littleEndian);
28136         }
28137         values = [];
28138         for (i = 0; i < length; i += 1) {
28139             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28140         }
28141         
28142         if (tagType.ascii) {
28143             str = '';
28144             // Concatenate the chars:
28145             for (i = 0; i < values.length; i += 1) {
28146                 c = values[i];
28147                 // Ignore the terminating NULL byte(s):
28148                 if (c === '\u0000') {
28149                     break;
28150                 }
28151                 str += c;
28152             }
28153             return str;
28154         }
28155         return values;
28156     }
28157     
28158 });
28159
28160 Roo.apply(Roo.bootstrap.UploadCropbox, {
28161     tags : {
28162         'Orientation': 0x0112
28163     },
28164     
28165     Orientation: {
28166             1: 0, //'top-left',
28167 //            2: 'top-right',
28168             3: 180, //'bottom-right',
28169 //            4: 'bottom-left',
28170 //            5: 'left-top',
28171             6: 90, //'right-top',
28172 //            7: 'right-bottom',
28173             8: 270 //'left-bottom'
28174     },
28175     
28176     exifTagTypes : {
28177         // byte, 8-bit unsigned int:
28178         1: {
28179             getValue: function (dataView, dataOffset) {
28180                 return dataView.getUint8(dataOffset);
28181             },
28182             size: 1
28183         },
28184         // ascii, 8-bit byte:
28185         2: {
28186             getValue: function (dataView, dataOffset) {
28187                 return String.fromCharCode(dataView.getUint8(dataOffset));
28188             },
28189             size: 1,
28190             ascii: true
28191         },
28192         // short, 16 bit int:
28193         3: {
28194             getValue: function (dataView, dataOffset, littleEndian) {
28195                 return dataView.getUint16(dataOffset, littleEndian);
28196             },
28197             size: 2
28198         },
28199         // long, 32 bit int:
28200         4: {
28201             getValue: function (dataView, dataOffset, littleEndian) {
28202                 return dataView.getUint32(dataOffset, littleEndian);
28203             },
28204             size: 4
28205         },
28206         // rational = two long values, first is numerator, second is denominator:
28207         5: {
28208             getValue: function (dataView, dataOffset, littleEndian) {
28209                 return dataView.getUint32(dataOffset, littleEndian) /
28210                     dataView.getUint32(dataOffset + 4, littleEndian);
28211             },
28212             size: 8
28213         },
28214         // slong, 32 bit signed int:
28215         9: {
28216             getValue: function (dataView, dataOffset, littleEndian) {
28217                 return dataView.getInt32(dataOffset, littleEndian);
28218             },
28219             size: 4
28220         },
28221         // srational, two slongs, first is numerator, second is denominator:
28222         10: {
28223             getValue: function (dataView, dataOffset, littleEndian) {
28224                 return dataView.getInt32(dataOffset, littleEndian) /
28225                     dataView.getInt32(dataOffset + 4, littleEndian);
28226             },
28227             size: 8
28228         }
28229     },
28230     
28231     footer : {
28232         STANDARD : [
28233             {
28234                 tag : 'div',
28235                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28236                 action : 'rotate-left',
28237                 cn : [
28238                     {
28239                         tag : 'button',
28240                         cls : 'btn btn-default',
28241                         html : '<i class="fa fa-undo"></i>'
28242                     }
28243                 ]
28244             },
28245             {
28246                 tag : 'div',
28247                 cls : 'btn-group roo-upload-cropbox-picture',
28248                 action : 'picture',
28249                 cn : [
28250                     {
28251                         tag : 'button',
28252                         cls : 'btn btn-default',
28253                         html : '<i class="fa fa-picture-o"></i>'
28254                     }
28255                 ]
28256             },
28257             {
28258                 tag : 'div',
28259                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28260                 action : 'rotate-right',
28261                 cn : [
28262                     {
28263                         tag : 'button',
28264                         cls : 'btn btn-default',
28265                         html : '<i class="fa fa-repeat"></i>'
28266                     }
28267                 ]
28268             }
28269         ],
28270         DOCUMENT : [
28271             {
28272                 tag : 'div',
28273                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28274                 action : 'rotate-left',
28275                 cn : [
28276                     {
28277                         tag : 'button',
28278                         cls : 'btn btn-default',
28279                         html : '<i class="fa fa-undo"></i>'
28280                     }
28281                 ]
28282             },
28283             {
28284                 tag : 'div',
28285                 cls : 'btn-group roo-upload-cropbox-download',
28286                 action : 'download',
28287                 cn : [
28288                     {
28289                         tag : 'button',
28290                         cls : 'btn btn-default',
28291                         html : '<i class="fa fa-download"></i>'
28292                     }
28293                 ]
28294             },
28295             {
28296                 tag : 'div',
28297                 cls : 'btn-group roo-upload-cropbox-crop',
28298                 action : 'crop',
28299                 cn : [
28300                     {
28301                         tag : 'button',
28302                         cls : 'btn btn-default',
28303                         html : '<i class="fa fa-crop"></i>'
28304                     }
28305                 ]
28306             },
28307             {
28308                 tag : 'div',
28309                 cls : 'btn-group roo-upload-cropbox-trash',
28310                 action : 'trash',
28311                 cn : [
28312                     {
28313                         tag : 'button',
28314                         cls : 'btn btn-default',
28315                         html : '<i class="fa fa-trash"></i>'
28316                     }
28317                 ]
28318             },
28319             {
28320                 tag : 'div',
28321                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28322                 action : 'rotate-right',
28323                 cn : [
28324                     {
28325                         tag : 'button',
28326                         cls : 'btn btn-default',
28327                         html : '<i class="fa fa-repeat"></i>'
28328                     }
28329                 ]
28330             }
28331         ],
28332         ROTATOR : [
28333             {
28334                 tag : 'div',
28335                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28336                 action : 'rotate-left',
28337                 cn : [
28338                     {
28339                         tag : 'button',
28340                         cls : 'btn btn-default',
28341                         html : '<i class="fa fa-undo"></i>'
28342                     }
28343                 ]
28344             },
28345             {
28346                 tag : 'div',
28347                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28348                 action : 'rotate-right',
28349                 cn : [
28350                     {
28351                         tag : 'button',
28352                         cls : 'btn btn-default',
28353                         html : '<i class="fa fa-repeat"></i>'
28354                     }
28355                 ]
28356             }
28357         ]
28358     }
28359 });
28360
28361 /*
28362 * Licence: LGPL
28363 */
28364
28365 /**
28366  * @class Roo.bootstrap.DocumentManager
28367  * @extends Roo.bootstrap.Component
28368  * Bootstrap DocumentManager class
28369  * @cfg {String} paramName default 'imageUpload'
28370  * @cfg {String} toolTipName default 'filename'
28371  * @cfg {String} method default POST
28372  * @cfg {String} url action url
28373  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28374  * @cfg {Boolean} multiple multiple upload default true
28375  * @cfg {Number} thumbSize default 300
28376  * @cfg {String} fieldLabel
28377  * @cfg {Number} labelWidth default 4
28378  * @cfg {String} labelAlign (left|top) default left
28379  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28380 * @cfg {Number} labellg set the width of label (1-12)
28381  * @cfg {Number} labelmd set the width of label (1-12)
28382  * @cfg {Number} labelsm set the width of label (1-12)
28383  * @cfg {Number} labelxs set the width of label (1-12)
28384  * 
28385  * @constructor
28386  * Create a new DocumentManager
28387  * @param {Object} config The config object
28388  */
28389
28390 Roo.bootstrap.DocumentManager = function(config){
28391     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28392     
28393     this.files = [];
28394     this.delegates = [];
28395     
28396     this.addEvents({
28397         /**
28398          * @event initial
28399          * Fire when initial the DocumentManager
28400          * @param {Roo.bootstrap.DocumentManager} this
28401          */
28402         "initial" : true,
28403         /**
28404          * @event inspect
28405          * inspect selected file
28406          * @param {Roo.bootstrap.DocumentManager} this
28407          * @param {File} file
28408          */
28409         "inspect" : true,
28410         /**
28411          * @event exception
28412          * Fire when xhr load exception
28413          * @param {Roo.bootstrap.DocumentManager} this
28414          * @param {XMLHttpRequest} xhr
28415          */
28416         "exception" : true,
28417         /**
28418          * @event afterupload
28419          * Fire when xhr load exception
28420          * @param {Roo.bootstrap.DocumentManager} this
28421          * @param {XMLHttpRequest} xhr
28422          */
28423         "afterupload" : true,
28424         /**
28425          * @event prepare
28426          * prepare the form data
28427          * @param {Roo.bootstrap.DocumentManager} this
28428          * @param {Object} formData
28429          */
28430         "prepare" : true,
28431         /**
28432          * @event remove
28433          * Fire when remove the file
28434          * @param {Roo.bootstrap.DocumentManager} this
28435          * @param {Object} file
28436          */
28437         "remove" : true,
28438         /**
28439          * @event refresh
28440          * Fire after refresh the file
28441          * @param {Roo.bootstrap.DocumentManager} this
28442          */
28443         "refresh" : true,
28444         /**
28445          * @event click
28446          * Fire after click the image
28447          * @param {Roo.bootstrap.DocumentManager} this
28448          * @param {Object} file
28449          */
28450         "click" : true,
28451         /**
28452          * @event edit
28453          * Fire when upload a image and editable set to true
28454          * @param {Roo.bootstrap.DocumentManager} this
28455          * @param {Object} file
28456          */
28457         "edit" : true,
28458         /**
28459          * @event beforeselectfile
28460          * Fire before select file
28461          * @param {Roo.bootstrap.DocumentManager} this
28462          */
28463         "beforeselectfile" : true,
28464         /**
28465          * @event process
28466          * Fire before process file
28467          * @param {Roo.bootstrap.DocumentManager} this
28468          * @param {Object} file
28469          */
28470         "process" : true,
28471         /**
28472          * @event previewrendered
28473          * Fire when preview rendered
28474          * @param {Roo.bootstrap.DocumentManager} this
28475          * @param {Object} file
28476          */
28477         "previewrendered" : true
28478         
28479     });
28480 };
28481
28482 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28483     
28484     boxes : 0,
28485     inputName : '',
28486     thumbSize : 300,
28487     multiple : true,
28488     files : false,
28489     method : 'POST',
28490     url : '',
28491     paramName : 'imageUpload',
28492     toolTipName : 'filename',
28493     fieldLabel : '',
28494     labelWidth : 4,
28495     labelAlign : 'left',
28496     editable : true,
28497     delegates : false,
28498     xhr : false, 
28499     
28500     labellg : 0,
28501     labelmd : 0,
28502     labelsm : 0,
28503     labelxs : 0,
28504     
28505     getAutoCreate : function()
28506     {   
28507         var managerWidget = {
28508             tag : 'div',
28509             cls : 'roo-document-manager',
28510             cn : [
28511                 {
28512                     tag : 'input',
28513                     cls : 'roo-document-manager-selector',
28514                     type : 'file'
28515                 },
28516                 {
28517                     tag : 'div',
28518                     cls : 'roo-document-manager-uploader',
28519                     cn : [
28520                         {
28521                             tag : 'div',
28522                             cls : 'roo-document-manager-upload-btn',
28523                             html : '<i class="fa fa-plus"></i>'
28524                         }
28525                     ]
28526                     
28527                 }
28528             ]
28529         };
28530         
28531         var content = [
28532             {
28533                 tag : 'div',
28534                 cls : 'column col-md-12',
28535                 cn : managerWidget
28536             }
28537         ];
28538         
28539         if(this.fieldLabel.length){
28540             
28541             content = [
28542                 {
28543                     tag : 'div',
28544                     cls : 'column col-md-12',
28545                     html : this.fieldLabel
28546                 },
28547                 {
28548                     tag : 'div',
28549                     cls : 'column col-md-12',
28550                     cn : managerWidget
28551                 }
28552             ];
28553
28554             if(this.labelAlign == 'left'){
28555                 content = [
28556                     {
28557                         tag : 'div',
28558                         cls : 'column',
28559                         html : this.fieldLabel
28560                     },
28561                     {
28562                         tag : 'div',
28563                         cls : 'column',
28564                         cn : managerWidget
28565                     }
28566                 ];
28567                 
28568                 if(this.labelWidth > 12){
28569                     content[0].style = "width: " + this.labelWidth + 'px';
28570                 }
28571
28572                 if(this.labelWidth < 13 && this.labelmd == 0){
28573                     this.labelmd = this.labelWidth;
28574                 }
28575
28576                 if(this.labellg > 0){
28577                     content[0].cls += ' col-lg-' + this.labellg;
28578                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28579                 }
28580
28581                 if(this.labelmd > 0){
28582                     content[0].cls += ' col-md-' + this.labelmd;
28583                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28584                 }
28585
28586                 if(this.labelsm > 0){
28587                     content[0].cls += ' col-sm-' + this.labelsm;
28588                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28589                 }
28590
28591                 if(this.labelxs > 0){
28592                     content[0].cls += ' col-xs-' + this.labelxs;
28593                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28594                 }
28595                 
28596             }
28597         }
28598         
28599         var cfg = {
28600             tag : 'div',
28601             cls : 'row clearfix',
28602             cn : content
28603         };
28604         
28605         return cfg;
28606         
28607     },
28608     
28609     initEvents : function()
28610     {
28611         this.managerEl = this.el.select('.roo-document-manager', true).first();
28612         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28613         
28614         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28615         this.selectorEl.hide();
28616         
28617         if(this.multiple){
28618             this.selectorEl.attr('multiple', 'multiple');
28619         }
28620         
28621         this.selectorEl.on('change', this.onFileSelected, this);
28622         
28623         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28624         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28625         
28626         this.uploader.on('click', this.onUploaderClick, this);
28627         
28628         this.renderProgressDialog();
28629         
28630         var _this = this;
28631         
28632         window.addEventListener("resize", function() { _this.refresh(); } );
28633         
28634         this.fireEvent('initial', this);
28635     },
28636     
28637     renderProgressDialog : function()
28638     {
28639         var _this = this;
28640         
28641         this.progressDialog = new Roo.bootstrap.Modal({
28642             cls : 'roo-document-manager-progress-dialog',
28643             allow_close : false,
28644             title : '',
28645             buttons : [
28646                 {
28647                     name  :'cancel',
28648                     weight : 'danger',
28649                     html : 'Cancel'
28650                 }
28651             ], 
28652             listeners : { 
28653                 btnclick : function() {
28654                     _this.uploadCancel();
28655                     this.hide();
28656                 }
28657             }
28658         });
28659          
28660         this.progressDialog.render(Roo.get(document.body));
28661          
28662         this.progress = new Roo.bootstrap.Progress({
28663             cls : 'roo-document-manager-progress',
28664             active : true,
28665             striped : true
28666         });
28667         
28668         this.progress.render(this.progressDialog.getChildContainer());
28669         
28670         this.progressBar = new Roo.bootstrap.ProgressBar({
28671             cls : 'roo-document-manager-progress-bar',
28672             aria_valuenow : 0,
28673             aria_valuemin : 0,
28674             aria_valuemax : 12,
28675             panel : 'success'
28676         });
28677         
28678         this.progressBar.render(this.progress.getChildContainer());
28679     },
28680     
28681     onUploaderClick : function(e)
28682     {
28683         e.preventDefault();
28684      
28685         if(this.fireEvent('beforeselectfile', this) != false){
28686             this.selectorEl.dom.click();
28687         }
28688         
28689     },
28690     
28691     onFileSelected : function(e)
28692     {
28693         e.preventDefault();
28694         
28695         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28696             return;
28697         }
28698         
28699         Roo.each(this.selectorEl.dom.files, function(file){
28700             if(this.fireEvent('inspect', this, file) != false){
28701                 this.files.push(file);
28702             }
28703         }, this);
28704         
28705         this.queue();
28706         
28707     },
28708     
28709     queue : function()
28710     {
28711         this.selectorEl.dom.value = '';
28712         
28713         if(!this.files || !this.files.length){
28714             return;
28715         }
28716         
28717         if(this.boxes > 0 && this.files.length > this.boxes){
28718             this.files = this.files.slice(0, this.boxes);
28719         }
28720         
28721         this.uploader.show();
28722         
28723         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28724             this.uploader.hide();
28725         }
28726         
28727         var _this = this;
28728         
28729         var files = [];
28730         
28731         var docs = [];
28732         
28733         Roo.each(this.files, function(file){
28734             
28735             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28736                 var f = this.renderPreview(file);
28737                 files.push(f);
28738                 return;
28739             }
28740             
28741             if(file.type.indexOf('image') != -1){
28742                 this.delegates.push(
28743                     (function(){
28744                         _this.process(file);
28745                     }).createDelegate(this)
28746                 );
28747         
28748                 return;
28749             }
28750             
28751             docs.push(
28752                 (function(){
28753                     _this.process(file);
28754                 }).createDelegate(this)
28755             );
28756             
28757         }, this);
28758         
28759         this.files = files;
28760         
28761         this.delegates = this.delegates.concat(docs);
28762         
28763         if(!this.delegates.length){
28764             this.refresh();
28765             return;
28766         }
28767         
28768         this.progressBar.aria_valuemax = this.delegates.length;
28769         
28770         this.arrange();
28771         
28772         return;
28773     },
28774     
28775     arrange : function()
28776     {
28777         if(!this.delegates.length){
28778             this.progressDialog.hide();
28779             this.refresh();
28780             return;
28781         }
28782         
28783         var delegate = this.delegates.shift();
28784         
28785         this.progressDialog.show();
28786         
28787         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28788         
28789         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28790         
28791         delegate();
28792     },
28793     
28794     refresh : function()
28795     {
28796         this.uploader.show();
28797         
28798         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28799             this.uploader.hide();
28800         }
28801         
28802         Roo.isTouch ? this.closable(false) : this.closable(true);
28803         
28804         this.fireEvent('refresh', this);
28805     },
28806     
28807     onRemove : function(e, el, o)
28808     {
28809         e.preventDefault();
28810         
28811         this.fireEvent('remove', this, o);
28812         
28813     },
28814     
28815     remove : function(o)
28816     {
28817         var files = [];
28818         
28819         Roo.each(this.files, function(file){
28820             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28821                 files.push(file);
28822                 return;
28823             }
28824
28825             o.target.remove();
28826
28827         }, this);
28828         
28829         this.files = files;
28830         
28831         this.refresh();
28832     },
28833     
28834     clear : function()
28835     {
28836         Roo.each(this.files, function(file){
28837             if(!file.target){
28838                 return;
28839             }
28840             
28841             file.target.remove();
28842
28843         }, this);
28844         
28845         this.files = [];
28846         
28847         this.refresh();
28848     },
28849     
28850     onClick : function(e, el, o)
28851     {
28852         e.preventDefault();
28853         
28854         this.fireEvent('click', this, o);
28855         
28856     },
28857     
28858     closable : function(closable)
28859     {
28860         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28861             
28862             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28863             
28864             if(closable){
28865                 el.show();
28866                 return;
28867             }
28868             
28869             el.hide();
28870             
28871         }, this);
28872     },
28873     
28874     xhrOnLoad : function(xhr)
28875     {
28876         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28877             el.remove();
28878         }, this);
28879         
28880         if (xhr.readyState !== 4) {
28881             this.arrange();
28882             this.fireEvent('exception', this, xhr);
28883             return;
28884         }
28885
28886         var response = Roo.decode(xhr.responseText);
28887         
28888         if(!response.success){
28889             this.arrange();
28890             this.fireEvent('exception', this, xhr);
28891             return;
28892         }
28893         
28894         var file = this.renderPreview(response.data);
28895         
28896         this.files.push(file);
28897         
28898         this.arrange();
28899         
28900         this.fireEvent('afterupload', this, xhr);
28901         
28902     },
28903     
28904     xhrOnError : function(xhr)
28905     {
28906         Roo.log('xhr on error');
28907         
28908         var response = Roo.decode(xhr.responseText);
28909           
28910         Roo.log(response);
28911         
28912         this.arrange();
28913     },
28914     
28915     process : function(file)
28916     {
28917         if(this.fireEvent('process', this, file) !== false){
28918             if(this.editable && file.type.indexOf('image') != -1){
28919                 this.fireEvent('edit', this, file);
28920                 return;
28921             }
28922
28923             this.uploadStart(file, false);
28924
28925             return;
28926         }
28927         
28928     },
28929     
28930     uploadStart : function(file, crop)
28931     {
28932         this.xhr = new XMLHttpRequest();
28933         
28934         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28935             this.arrange();
28936             return;
28937         }
28938         
28939         file.xhr = this.xhr;
28940             
28941         this.managerEl.createChild({
28942             tag : 'div',
28943             cls : 'roo-document-manager-loading',
28944             cn : [
28945                 {
28946                     tag : 'div',
28947                     tooltip : file.name,
28948                     cls : 'roo-document-manager-thumb',
28949                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28950                 }
28951             ]
28952
28953         });
28954
28955         this.xhr.open(this.method, this.url, true);
28956         
28957         var headers = {
28958             "Accept": "application/json",
28959             "Cache-Control": "no-cache",
28960             "X-Requested-With": "XMLHttpRequest"
28961         };
28962         
28963         for (var headerName in headers) {
28964             var headerValue = headers[headerName];
28965             if (headerValue) {
28966                 this.xhr.setRequestHeader(headerName, headerValue);
28967             }
28968         }
28969         
28970         var _this = this;
28971         
28972         this.xhr.onload = function()
28973         {
28974             _this.xhrOnLoad(_this.xhr);
28975         }
28976         
28977         this.xhr.onerror = function()
28978         {
28979             _this.xhrOnError(_this.xhr);
28980         }
28981         
28982         var formData = new FormData();
28983
28984         formData.append('returnHTML', 'NO');
28985         
28986         if(crop){
28987             formData.append('crop', crop);
28988         }
28989         
28990         formData.append(this.paramName, file, file.name);
28991         
28992         var options = {
28993             file : file, 
28994             manually : false
28995         };
28996         
28997         if(this.fireEvent('prepare', this, formData, options) != false){
28998             
28999             if(options.manually){
29000                 return;
29001             }
29002             
29003             this.xhr.send(formData);
29004             return;
29005         };
29006         
29007         this.uploadCancel();
29008     },
29009     
29010     uploadCancel : function()
29011     {
29012         if (this.xhr) {
29013             this.xhr.abort();
29014         }
29015         
29016         this.delegates = [];
29017         
29018         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29019             el.remove();
29020         }, this);
29021         
29022         this.arrange();
29023     },
29024     
29025     renderPreview : function(file)
29026     {
29027         if(typeof(file.target) != 'undefined' && file.target){
29028             return file;
29029         }
29030         
29031         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29032         
29033         var previewEl = this.managerEl.createChild({
29034             tag : 'div',
29035             cls : 'roo-document-manager-preview',
29036             cn : [
29037                 {
29038                     tag : 'div',
29039                     tooltip : file[this.toolTipName],
29040                     cls : 'roo-document-manager-thumb',
29041                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29042                 },
29043                 {
29044                     tag : 'button',
29045                     cls : 'close',
29046                     html : '<i class="fa fa-times-circle"></i>'
29047                 }
29048             ]
29049         });
29050
29051         var close = previewEl.select('button.close', true).first();
29052
29053         close.on('click', this.onRemove, this, file);
29054
29055         file.target = previewEl;
29056
29057         var image = previewEl.select('img', true).first();
29058         
29059         var _this = this;
29060         
29061         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29062         
29063         image.on('click', this.onClick, this, file);
29064         
29065         this.fireEvent('previewrendered', this, file);
29066         
29067         return file;
29068         
29069     },
29070     
29071     onPreviewLoad : function(file, image)
29072     {
29073         if(typeof(file.target) == 'undefined' || !file.target){
29074             return;
29075         }
29076         
29077         var width = image.dom.naturalWidth || image.dom.width;
29078         var height = image.dom.naturalHeight || image.dom.height;
29079         
29080         if(width > height){
29081             file.target.addClass('wide');
29082             return;
29083         }
29084         
29085         file.target.addClass('tall');
29086         return;
29087         
29088     },
29089     
29090     uploadFromSource : function(file, crop)
29091     {
29092         this.xhr = new XMLHttpRequest();
29093         
29094         this.managerEl.createChild({
29095             tag : 'div',
29096             cls : 'roo-document-manager-loading',
29097             cn : [
29098                 {
29099                     tag : 'div',
29100                     tooltip : file.name,
29101                     cls : 'roo-document-manager-thumb',
29102                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29103                 }
29104             ]
29105
29106         });
29107
29108         this.xhr.open(this.method, this.url, true);
29109         
29110         var headers = {
29111             "Accept": "application/json",
29112             "Cache-Control": "no-cache",
29113             "X-Requested-With": "XMLHttpRequest"
29114         };
29115         
29116         for (var headerName in headers) {
29117             var headerValue = headers[headerName];
29118             if (headerValue) {
29119                 this.xhr.setRequestHeader(headerName, headerValue);
29120             }
29121         }
29122         
29123         var _this = this;
29124         
29125         this.xhr.onload = function()
29126         {
29127             _this.xhrOnLoad(_this.xhr);
29128         }
29129         
29130         this.xhr.onerror = function()
29131         {
29132             _this.xhrOnError(_this.xhr);
29133         }
29134         
29135         var formData = new FormData();
29136
29137         formData.append('returnHTML', 'NO');
29138         
29139         formData.append('crop', crop);
29140         
29141         if(typeof(file.filename) != 'undefined'){
29142             formData.append('filename', file.filename);
29143         }
29144         
29145         if(typeof(file.mimetype) != 'undefined'){
29146             formData.append('mimetype', file.mimetype);
29147         }
29148         
29149         Roo.log(formData);
29150         
29151         if(this.fireEvent('prepare', this, formData) != false){
29152             this.xhr.send(formData);
29153         };
29154     }
29155 });
29156
29157 /*
29158 * Licence: LGPL
29159 */
29160
29161 /**
29162  * @class Roo.bootstrap.DocumentViewer
29163  * @extends Roo.bootstrap.Component
29164  * Bootstrap DocumentViewer class
29165  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29166  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29167  * 
29168  * @constructor
29169  * Create a new DocumentViewer
29170  * @param {Object} config The config object
29171  */
29172
29173 Roo.bootstrap.DocumentViewer = function(config){
29174     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29175     
29176     this.addEvents({
29177         /**
29178          * @event initial
29179          * Fire after initEvent
29180          * @param {Roo.bootstrap.DocumentViewer} this
29181          */
29182         "initial" : true,
29183         /**
29184          * @event click
29185          * Fire after click
29186          * @param {Roo.bootstrap.DocumentViewer} this
29187          */
29188         "click" : true,
29189         /**
29190          * @event download
29191          * Fire after download button
29192          * @param {Roo.bootstrap.DocumentViewer} this
29193          */
29194         "download" : true,
29195         /**
29196          * @event trash
29197          * Fire after trash button
29198          * @param {Roo.bootstrap.DocumentViewer} this
29199          */
29200         "trash" : true
29201         
29202     });
29203 };
29204
29205 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29206     
29207     showDownload : true,
29208     
29209     showTrash : true,
29210     
29211     getAutoCreate : function()
29212     {
29213         var cfg = {
29214             tag : 'div',
29215             cls : 'roo-document-viewer',
29216             cn : [
29217                 {
29218                     tag : 'div',
29219                     cls : 'roo-document-viewer-body',
29220                     cn : [
29221                         {
29222                             tag : 'div',
29223                             cls : 'roo-document-viewer-thumb',
29224                             cn : [
29225                                 {
29226                                     tag : 'img',
29227                                     cls : 'roo-document-viewer-image'
29228                                 }
29229                             ]
29230                         }
29231                     ]
29232                 },
29233                 {
29234                     tag : 'div',
29235                     cls : 'roo-document-viewer-footer',
29236                     cn : {
29237                         tag : 'div',
29238                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29239                         cn : [
29240                             {
29241                                 tag : 'div',
29242                                 cls : 'btn-group roo-document-viewer-download',
29243                                 cn : [
29244                                     {
29245                                         tag : 'button',
29246                                         cls : 'btn btn-default',
29247                                         html : '<i class="fa fa-download"></i>'
29248                                     }
29249                                 ]
29250                             },
29251                             {
29252                                 tag : 'div',
29253                                 cls : 'btn-group roo-document-viewer-trash',
29254                                 cn : [
29255                                     {
29256                                         tag : 'button',
29257                                         cls : 'btn btn-default',
29258                                         html : '<i class="fa fa-trash"></i>'
29259                                     }
29260                                 ]
29261                             }
29262                         ]
29263                     }
29264                 }
29265             ]
29266         };
29267         
29268         return cfg;
29269     },
29270     
29271     initEvents : function()
29272     {
29273         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29274         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29275         
29276         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29277         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29278         
29279         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29280         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29281         
29282         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29283         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29284         
29285         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29286         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29287         
29288         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29289         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29290         
29291         this.bodyEl.on('click', this.onClick, this);
29292         this.downloadBtn.on('click', this.onDownload, this);
29293         this.trashBtn.on('click', this.onTrash, this);
29294         
29295         this.downloadBtn.hide();
29296         this.trashBtn.hide();
29297         
29298         if(this.showDownload){
29299             this.downloadBtn.show();
29300         }
29301         
29302         if(this.showTrash){
29303             this.trashBtn.show();
29304         }
29305         
29306         if(!this.showDownload && !this.showTrash) {
29307             this.footerEl.hide();
29308         }
29309         
29310     },
29311     
29312     initial : function()
29313     {
29314         this.fireEvent('initial', this);
29315         
29316     },
29317     
29318     onClick : function(e)
29319     {
29320         e.preventDefault();
29321         
29322         this.fireEvent('click', this);
29323     },
29324     
29325     onDownload : function(e)
29326     {
29327         e.preventDefault();
29328         
29329         this.fireEvent('download', this);
29330     },
29331     
29332     onTrash : function(e)
29333     {
29334         e.preventDefault();
29335         
29336         this.fireEvent('trash', this);
29337     }
29338     
29339 });
29340 /*
29341  * - LGPL
29342  *
29343  * nav progress bar
29344  * 
29345  */
29346
29347 /**
29348  * @class Roo.bootstrap.NavProgressBar
29349  * @extends Roo.bootstrap.Component
29350  * Bootstrap NavProgressBar class
29351  * 
29352  * @constructor
29353  * Create a new nav progress bar
29354  * @param {Object} config The config object
29355  */
29356
29357 Roo.bootstrap.NavProgressBar = function(config){
29358     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29359
29360     this.bullets = this.bullets || [];
29361    
29362 //    Roo.bootstrap.NavProgressBar.register(this);
29363      this.addEvents({
29364         /**
29365              * @event changed
29366              * Fires when the active item changes
29367              * @param {Roo.bootstrap.NavProgressBar} this
29368              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29369              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29370          */
29371         'changed': true
29372      });
29373     
29374 };
29375
29376 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29377     
29378     bullets : [],
29379     barItems : [],
29380     
29381     getAutoCreate : function()
29382     {
29383         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29384         
29385         cfg = {
29386             tag : 'div',
29387             cls : 'roo-navigation-bar-group',
29388             cn : [
29389                 {
29390                     tag : 'div',
29391                     cls : 'roo-navigation-top-bar'
29392                 },
29393                 {
29394                     tag : 'div',
29395                     cls : 'roo-navigation-bullets-bar',
29396                     cn : [
29397                         {
29398                             tag : 'ul',
29399                             cls : 'roo-navigation-bar'
29400                         }
29401                     ]
29402                 },
29403                 
29404                 {
29405                     tag : 'div',
29406                     cls : 'roo-navigation-bottom-bar'
29407                 }
29408             ]
29409             
29410         };
29411         
29412         return cfg;
29413         
29414     },
29415     
29416     initEvents: function() 
29417     {
29418         
29419     },
29420     
29421     onRender : function(ct, position) 
29422     {
29423         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29424         
29425         if(this.bullets.length){
29426             Roo.each(this.bullets, function(b){
29427                this.addItem(b);
29428             }, this);
29429         }
29430         
29431         this.format();
29432         
29433     },
29434     
29435     addItem : function(cfg)
29436     {
29437         var item = new Roo.bootstrap.NavProgressItem(cfg);
29438         
29439         item.parentId = this.id;
29440         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29441         
29442         if(cfg.html){
29443             var top = new Roo.bootstrap.Element({
29444                 tag : 'div',
29445                 cls : 'roo-navigation-bar-text'
29446             });
29447             
29448             var bottom = new Roo.bootstrap.Element({
29449                 tag : 'div',
29450                 cls : 'roo-navigation-bar-text'
29451             });
29452             
29453             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29454             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29455             
29456             var topText = new Roo.bootstrap.Element({
29457                 tag : 'span',
29458                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29459             });
29460             
29461             var bottomText = new Roo.bootstrap.Element({
29462                 tag : 'span',
29463                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29464             });
29465             
29466             topText.onRender(top.el, null);
29467             bottomText.onRender(bottom.el, null);
29468             
29469             item.topEl = top;
29470             item.bottomEl = bottom;
29471         }
29472         
29473         this.barItems.push(item);
29474         
29475         return item;
29476     },
29477     
29478     getActive : function()
29479     {
29480         var active = false;
29481         
29482         Roo.each(this.barItems, function(v){
29483             
29484             if (!v.isActive()) {
29485                 return;
29486             }
29487             
29488             active = v;
29489             return false;
29490             
29491         });
29492         
29493         return active;
29494     },
29495     
29496     setActiveItem : function(item)
29497     {
29498         var prev = false;
29499         
29500         Roo.each(this.barItems, function(v){
29501             if (v.rid == item.rid) {
29502                 return ;
29503             }
29504             
29505             if (v.isActive()) {
29506                 v.setActive(false);
29507                 prev = v;
29508             }
29509         });
29510
29511         item.setActive(true);
29512         
29513         this.fireEvent('changed', this, item, prev);
29514     },
29515     
29516     getBarItem: function(rid)
29517     {
29518         var ret = false;
29519         
29520         Roo.each(this.barItems, function(e) {
29521             if (e.rid != rid) {
29522                 return;
29523             }
29524             
29525             ret =  e;
29526             return false;
29527         });
29528         
29529         return ret;
29530     },
29531     
29532     indexOfItem : function(item)
29533     {
29534         var index = false;
29535         
29536         Roo.each(this.barItems, function(v, i){
29537             
29538             if (v.rid != item.rid) {
29539                 return;
29540             }
29541             
29542             index = i;
29543             return false
29544         });
29545         
29546         return index;
29547     },
29548     
29549     setActiveNext : function()
29550     {
29551         var i = this.indexOfItem(this.getActive());
29552         
29553         if (i > this.barItems.length) {
29554             return;
29555         }
29556         
29557         this.setActiveItem(this.barItems[i+1]);
29558     },
29559     
29560     setActivePrev : function()
29561     {
29562         var i = this.indexOfItem(this.getActive());
29563         
29564         if (i  < 1) {
29565             return;
29566         }
29567         
29568         this.setActiveItem(this.barItems[i-1]);
29569     },
29570     
29571     format : function()
29572     {
29573         if(!this.barItems.length){
29574             return;
29575         }
29576      
29577         var width = 100 / this.barItems.length;
29578         
29579         Roo.each(this.barItems, function(i){
29580             i.el.setStyle('width', width + '%');
29581             i.topEl.el.setStyle('width', width + '%');
29582             i.bottomEl.el.setStyle('width', width + '%');
29583         }, this);
29584         
29585     }
29586     
29587 });
29588 /*
29589  * - LGPL
29590  *
29591  * Nav Progress Item
29592  * 
29593  */
29594
29595 /**
29596  * @class Roo.bootstrap.NavProgressItem
29597  * @extends Roo.bootstrap.Component
29598  * Bootstrap NavProgressItem class
29599  * @cfg {String} rid the reference id
29600  * @cfg {Boolean} active (true|false) Is item active default false
29601  * @cfg {Boolean} disabled (true|false) Is item active default false
29602  * @cfg {String} html
29603  * @cfg {String} position (top|bottom) text position default bottom
29604  * @cfg {String} icon show icon instead of number
29605  * 
29606  * @constructor
29607  * Create a new NavProgressItem
29608  * @param {Object} config The config object
29609  */
29610 Roo.bootstrap.NavProgressItem = function(config){
29611     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29612     this.addEvents({
29613         // raw events
29614         /**
29615          * @event click
29616          * The raw click event for the entire grid.
29617          * @param {Roo.bootstrap.NavProgressItem} this
29618          * @param {Roo.EventObject} e
29619          */
29620         "click" : true
29621     });
29622    
29623 };
29624
29625 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29626     
29627     rid : '',
29628     active : false,
29629     disabled : false,
29630     html : '',
29631     position : 'bottom',
29632     icon : false,
29633     
29634     getAutoCreate : function()
29635     {
29636         var iconCls = 'roo-navigation-bar-item-icon';
29637         
29638         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29639         
29640         var cfg = {
29641             tag: 'li',
29642             cls: 'roo-navigation-bar-item',
29643             cn : [
29644                 {
29645                     tag : 'i',
29646                     cls : iconCls
29647                 }
29648             ]
29649         };
29650         
29651         if(this.active){
29652             cfg.cls += ' active';
29653         }
29654         if(this.disabled){
29655             cfg.cls += ' disabled';
29656         }
29657         
29658         return cfg;
29659     },
29660     
29661     disable : function()
29662     {
29663         this.setDisabled(true);
29664     },
29665     
29666     enable : function()
29667     {
29668         this.setDisabled(false);
29669     },
29670     
29671     initEvents: function() 
29672     {
29673         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29674         
29675         this.iconEl.on('click', this.onClick, this);
29676     },
29677     
29678     onClick : function(e)
29679     {
29680         e.preventDefault();
29681         
29682         if(this.disabled){
29683             return;
29684         }
29685         
29686         if(this.fireEvent('click', this, e) === false){
29687             return;
29688         };
29689         
29690         this.parent().setActiveItem(this);
29691     },
29692     
29693     isActive: function () 
29694     {
29695         return this.active;
29696     },
29697     
29698     setActive : function(state)
29699     {
29700         if(this.active == state){
29701             return;
29702         }
29703         
29704         this.active = state;
29705         
29706         if (state) {
29707             this.el.addClass('active');
29708             return;
29709         }
29710         
29711         this.el.removeClass('active');
29712         
29713         return;
29714     },
29715     
29716     setDisabled : function(state)
29717     {
29718         if(this.disabled == state){
29719             return;
29720         }
29721         
29722         this.disabled = state;
29723         
29724         if (state) {
29725             this.el.addClass('disabled');
29726             return;
29727         }
29728         
29729         this.el.removeClass('disabled');
29730     },
29731     
29732     tooltipEl : function()
29733     {
29734         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29735     }
29736 });
29737  
29738
29739  /*
29740  * - LGPL
29741  *
29742  * FieldLabel
29743  * 
29744  */
29745
29746 /**
29747  * @class Roo.bootstrap.FieldLabel
29748  * @extends Roo.bootstrap.Component
29749  * Bootstrap FieldLabel class
29750  * @cfg {String} html contents of the element
29751  * @cfg {String} tag tag of the element default label
29752  * @cfg {String} cls class of the element
29753  * @cfg {String} target label target 
29754  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29755  * @cfg {String} invalidClass default "text-warning"
29756  * @cfg {String} validClass default "text-success"
29757  * @cfg {String} iconTooltip default "This field is required"
29758  * @cfg {String} indicatorpos (left|right) default left
29759  * 
29760  * @constructor
29761  * Create a new FieldLabel
29762  * @param {Object} config The config object
29763  */
29764
29765 Roo.bootstrap.FieldLabel = function(config){
29766     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29767     
29768     this.addEvents({
29769             /**
29770              * @event invalid
29771              * Fires after the field has been marked as invalid.
29772              * @param {Roo.form.FieldLabel} this
29773              * @param {String} msg The validation message
29774              */
29775             invalid : true,
29776             /**
29777              * @event valid
29778              * Fires after the field has been validated with no errors.
29779              * @param {Roo.form.FieldLabel} this
29780              */
29781             valid : true
29782         });
29783 };
29784
29785 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29786     
29787     tag: 'label',
29788     cls: '',
29789     html: '',
29790     target: '',
29791     allowBlank : true,
29792     invalidClass : 'has-warning',
29793     validClass : 'has-success',
29794     iconTooltip : 'This field is required',
29795     indicatorpos : 'left',
29796     
29797     getAutoCreate : function(){
29798         
29799         var cfg = {
29800             tag : this.tag,
29801             cls : 'roo-bootstrap-field-label ' + this.cls,
29802             for : this.target,
29803             cn : [
29804                 {
29805                     tag : 'i',
29806                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29807                     tooltip : this.iconTooltip
29808                 },
29809                 {
29810                     tag : 'span',
29811                     html : this.html
29812                 }
29813             ] 
29814         };
29815         
29816         if(this.indicatorpos == 'right'){
29817             var cfg = {
29818                 tag : this.tag,
29819                 cls : 'roo-bootstrap-field-label ' + this.cls,
29820                 for : this.target,
29821                 cn : [
29822                     {
29823                         tag : 'span',
29824                         html : this.html
29825                     },
29826                     {
29827                         tag : 'i',
29828                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29829                         tooltip : this.iconTooltip
29830                     }
29831                 ] 
29832             };
29833         }
29834         
29835         return cfg;
29836     },
29837     
29838     initEvents: function() 
29839     {
29840         Roo.bootstrap.Element.superclass.initEvents.call(this);
29841         
29842         this.indicator = this.indicatorEl();
29843         
29844         if(this.indicator){
29845             this.indicator.removeClass('visible');
29846             this.indicator.addClass('invisible');
29847         }
29848         
29849         Roo.bootstrap.FieldLabel.register(this);
29850     },
29851     
29852     indicatorEl : function()
29853     {
29854         var indicator = this.el.select('i.roo-required-indicator',true).first();
29855         
29856         if(!indicator){
29857             return false;
29858         }
29859         
29860         return indicator;
29861         
29862     },
29863     
29864     /**
29865      * Mark this field as valid
29866      */
29867     markValid : function()
29868     {
29869         if(this.indicator){
29870             this.indicator.removeClass('visible');
29871             this.indicator.addClass('invisible');
29872         }
29873         
29874         this.el.removeClass(this.invalidClass);
29875         
29876         this.el.addClass(this.validClass);
29877         
29878         this.fireEvent('valid', this);
29879     },
29880     
29881     /**
29882      * Mark this field as invalid
29883      * @param {String} msg The validation message
29884      */
29885     markInvalid : function(msg)
29886     {
29887         if(this.indicator){
29888             this.indicator.removeClass('invisible');
29889             this.indicator.addClass('visible');
29890         }
29891         
29892         this.el.removeClass(this.validClass);
29893         
29894         this.el.addClass(this.invalidClass);
29895         
29896         this.fireEvent('invalid', this, msg);
29897     }
29898     
29899    
29900 });
29901
29902 Roo.apply(Roo.bootstrap.FieldLabel, {
29903     
29904     groups: {},
29905     
29906      /**
29907     * register a FieldLabel Group
29908     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29909     */
29910     register : function(label)
29911     {
29912         if(this.groups.hasOwnProperty(label.target)){
29913             return;
29914         }
29915      
29916         this.groups[label.target] = label;
29917         
29918     },
29919     /**
29920     * fetch a FieldLabel Group based on the target
29921     * @param {string} target
29922     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29923     */
29924     get: function(target) {
29925         if (typeof(this.groups[target]) == 'undefined') {
29926             return false;
29927         }
29928         
29929         return this.groups[target] ;
29930     }
29931 });
29932
29933  
29934
29935  /*
29936  * - LGPL
29937  *
29938  * page DateSplitField.
29939  * 
29940  */
29941
29942
29943 /**
29944  * @class Roo.bootstrap.DateSplitField
29945  * @extends Roo.bootstrap.Component
29946  * Bootstrap DateSplitField class
29947  * @cfg {string} fieldLabel - the label associated
29948  * @cfg {Number} labelWidth set the width of label (0-12)
29949  * @cfg {String} labelAlign (top|left)
29950  * @cfg {Boolean} dayAllowBlank (true|false) default false
29951  * @cfg {Boolean} monthAllowBlank (true|false) default false
29952  * @cfg {Boolean} yearAllowBlank (true|false) default false
29953  * @cfg {string} dayPlaceholder 
29954  * @cfg {string} monthPlaceholder
29955  * @cfg {string} yearPlaceholder
29956  * @cfg {string} dayFormat default 'd'
29957  * @cfg {string} monthFormat default 'm'
29958  * @cfg {string} yearFormat default 'Y'
29959  * @cfg {Number} labellg set the width of label (1-12)
29960  * @cfg {Number} labelmd set the width of label (1-12)
29961  * @cfg {Number} labelsm set the width of label (1-12)
29962  * @cfg {Number} labelxs set the width of label (1-12)
29963
29964  *     
29965  * @constructor
29966  * Create a new DateSplitField
29967  * @param {Object} config The config object
29968  */
29969
29970 Roo.bootstrap.DateSplitField = function(config){
29971     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29972     
29973     this.addEvents({
29974         // raw events
29975          /**
29976          * @event years
29977          * getting the data of years
29978          * @param {Roo.bootstrap.DateSplitField} this
29979          * @param {Object} years
29980          */
29981         "years" : true,
29982         /**
29983          * @event days
29984          * getting the data of days
29985          * @param {Roo.bootstrap.DateSplitField} this
29986          * @param {Object} days
29987          */
29988         "days" : true,
29989         /**
29990          * @event invalid
29991          * Fires after the field has been marked as invalid.
29992          * @param {Roo.form.Field} this
29993          * @param {String} msg The validation message
29994          */
29995         invalid : true,
29996        /**
29997          * @event valid
29998          * Fires after the field has been validated with no errors.
29999          * @param {Roo.form.Field} this
30000          */
30001         valid : true
30002     });
30003 };
30004
30005 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30006     
30007     fieldLabel : '',
30008     labelAlign : 'top',
30009     labelWidth : 3,
30010     dayAllowBlank : false,
30011     monthAllowBlank : false,
30012     yearAllowBlank : false,
30013     dayPlaceholder : '',
30014     monthPlaceholder : '',
30015     yearPlaceholder : '',
30016     dayFormat : 'd',
30017     monthFormat : 'm',
30018     yearFormat : 'Y',
30019     isFormField : true,
30020     labellg : 0,
30021     labelmd : 0,
30022     labelsm : 0,
30023     labelxs : 0,
30024     
30025     getAutoCreate : function()
30026     {
30027         var cfg = {
30028             tag : 'div',
30029             cls : 'row roo-date-split-field-group',
30030             cn : [
30031                 {
30032                     tag : 'input',
30033                     type : 'hidden',
30034                     cls : 'form-hidden-field roo-date-split-field-group-value',
30035                     name : this.name
30036                 }
30037             ]
30038         };
30039         
30040         var labelCls = 'col-md-12';
30041         var contentCls = 'col-md-4';
30042         
30043         if(this.fieldLabel){
30044             
30045             var label = {
30046                 tag : 'div',
30047                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30048                 cn : [
30049                     {
30050                         tag : 'label',
30051                         html : this.fieldLabel
30052                     }
30053                 ]
30054             };
30055             
30056             if(this.labelAlign == 'left'){
30057             
30058                 if(this.labelWidth > 12){
30059                     label.style = "width: " + this.labelWidth + 'px';
30060                 }
30061
30062                 if(this.labelWidth < 13 && this.labelmd == 0){
30063                     this.labelmd = this.labelWidth;
30064                 }
30065
30066                 if(this.labellg > 0){
30067                     labelCls = ' col-lg-' + this.labellg;
30068                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30069                 }
30070
30071                 if(this.labelmd > 0){
30072                     labelCls = ' col-md-' + this.labelmd;
30073                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30074                 }
30075
30076                 if(this.labelsm > 0){
30077                     labelCls = ' col-sm-' + this.labelsm;
30078                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30079                 }
30080
30081                 if(this.labelxs > 0){
30082                     labelCls = ' col-xs-' + this.labelxs;
30083                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30084                 }
30085             }
30086             
30087             label.cls += ' ' + labelCls;
30088             
30089             cfg.cn.push(label);
30090         }
30091         
30092         Roo.each(['day', 'month', 'year'], function(t){
30093             cfg.cn.push({
30094                 tag : 'div',
30095                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30096             });
30097         }, this);
30098         
30099         return cfg;
30100     },
30101     
30102     inputEl: function ()
30103     {
30104         return this.el.select('.roo-date-split-field-group-value', true).first();
30105     },
30106     
30107     onRender : function(ct, position) 
30108     {
30109         var _this = this;
30110         
30111         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30112         
30113         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30114         
30115         this.dayField = new Roo.bootstrap.ComboBox({
30116             allowBlank : this.dayAllowBlank,
30117             alwaysQuery : true,
30118             displayField : 'value',
30119             editable : false,
30120             fieldLabel : '',
30121             forceSelection : true,
30122             mode : 'local',
30123             placeholder : this.dayPlaceholder,
30124             selectOnFocus : true,
30125             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30126             triggerAction : 'all',
30127             typeAhead : true,
30128             valueField : 'value',
30129             store : new Roo.data.SimpleStore({
30130                 data : (function() {    
30131                     var days = [];
30132                     _this.fireEvent('days', _this, days);
30133                     return days;
30134                 })(),
30135                 fields : [ 'value' ]
30136             }),
30137             listeners : {
30138                 select : function (_self, record, index)
30139                 {
30140                     _this.setValue(_this.getValue());
30141                 }
30142             }
30143         });
30144
30145         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30146         
30147         this.monthField = new Roo.bootstrap.MonthField({
30148             after : '<i class=\"fa fa-calendar\"></i>',
30149             allowBlank : this.monthAllowBlank,
30150             placeholder : this.monthPlaceholder,
30151             readOnly : true,
30152             listeners : {
30153                 render : function (_self)
30154                 {
30155                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30156                         e.preventDefault();
30157                         _self.focus();
30158                     });
30159                 },
30160                 select : function (_self, oldvalue, newvalue)
30161                 {
30162                     _this.setValue(_this.getValue());
30163                 }
30164             }
30165         });
30166         
30167         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30168         
30169         this.yearField = new Roo.bootstrap.ComboBox({
30170             allowBlank : this.yearAllowBlank,
30171             alwaysQuery : true,
30172             displayField : 'value',
30173             editable : false,
30174             fieldLabel : '',
30175             forceSelection : true,
30176             mode : 'local',
30177             placeholder : this.yearPlaceholder,
30178             selectOnFocus : true,
30179             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30180             triggerAction : 'all',
30181             typeAhead : true,
30182             valueField : 'value',
30183             store : new Roo.data.SimpleStore({
30184                 data : (function() {
30185                     var years = [];
30186                     _this.fireEvent('years', _this, years);
30187                     return years;
30188                 })(),
30189                 fields : [ 'value' ]
30190             }),
30191             listeners : {
30192                 select : function (_self, record, index)
30193                 {
30194                     _this.setValue(_this.getValue());
30195                 }
30196             }
30197         });
30198
30199         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30200     },
30201     
30202     setValue : function(v, format)
30203     {
30204         this.inputEl.dom.value = v;
30205         
30206         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30207         
30208         var d = Date.parseDate(v, f);
30209         
30210         if(!d){
30211             this.validate();
30212             return;
30213         }
30214         
30215         this.setDay(d.format(this.dayFormat));
30216         this.setMonth(d.format(this.monthFormat));
30217         this.setYear(d.format(this.yearFormat));
30218         
30219         this.validate();
30220         
30221         return;
30222     },
30223     
30224     setDay : function(v)
30225     {
30226         this.dayField.setValue(v);
30227         this.inputEl.dom.value = this.getValue();
30228         this.validate();
30229         return;
30230     },
30231     
30232     setMonth : function(v)
30233     {
30234         this.monthField.setValue(v, true);
30235         this.inputEl.dom.value = this.getValue();
30236         this.validate();
30237         return;
30238     },
30239     
30240     setYear : function(v)
30241     {
30242         this.yearField.setValue(v);
30243         this.inputEl.dom.value = this.getValue();
30244         this.validate();
30245         return;
30246     },
30247     
30248     getDay : function()
30249     {
30250         return this.dayField.getValue();
30251     },
30252     
30253     getMonth : function()
30254     {
30255         return this.monthField.getValue();
30256     },
30257     
30258     getYear : function()
30259     {
30260         return this.yearField.getValue();
30261     },
30262     
30263     getValue : function()
30264     {
30265         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30266         
30267         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30268         
30269         return date;
30270     },
30271     
30272     reset : function()
30273     {
30274         this.setDay('');
30275         this.setMonth('');
30276         this.setYear('');
30277         this.inputEl.dom.value = '';
30278         this.validate();
30279         return;
30280     },
30281     
30282     validate : function()
30283     {
30284         var d = this.dayField.validate();
30285         var m = this.monthField.validate();
30286         var y = this.yearField.validate();
30287         
30288         var valid = true;
30289         
30290         if(
30291                 (!this.dayAllowBlank && !d) ||
30292                 (!this.monthAllowBlank && !m) ||
30293                 (!this.yearAllowBlank && !y)
30294         ){
30295             valid = false;
30296         }
30297         
30298         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30299             return valid;
30300         }
30301         
30302         if(valid){
30303             this.markValid();
30304             return valid;
30305         }
30306         
30307         this.markInvalid();
30308         
30309         return valid;
30310     },
30311     
30312     markValid : function()
30313     {
30314         
30315         var label = this.el.select('label', true).first();
30316         var icon = this.el.select('i.fa-star', true).first();
30317
30318         if(label && icon){
30319             icon.remove();
30320         }
30321         
30322         this.fireEvent('valid', this);
30323     },
30324     
30325      /**
30326      * Mark this field as invalid
30327      * @param {String} msg The validation message
30328      */
30329     markInvalid : function(msg)
30330     {
30331         
30332         var label = this.el.select('label', true).first();
30333         var icon = this.el.select('i.fa-star', true).first();
30334
30335         if(label && !icon){
30336             this.el.select('.roo-date-split-field-label', true).createChild({
30337                 tag : 'i',
30338                 cls : 'text-danger fa fa-lg fa-star',
30339                 tooltip : 'This field is required',
30340                 style : 'margin-right:5px;'
30341             }, label, true);
30342         }
30343         
30344         this.fireEvent('invalid', this, msg);
30345     },
30346     
30347     clearInvalid : function()
30348     {
30349         var label = this.el.select('label', true).first();
30350         var icon = this.el.select('i.fa-star', true).first();
30351
30352         if(label && icon){
30353             icon.remove();
30354         }
30355         
30356         this.fireEvent('valid', this);
30357     },
30358     
30359     getName: function()
30360     {
30361         return this.name;
30362     }
30363     
30364 });
30365
30366  /**
30367  *
30368  * This is based on 
30369  * http://masonry.desandro.com
30370  *
30371  * The idea is to render all the bricks based on vertical width...
30372  *
30373  * The original code extends 'outlayer' - we might need to use that....
30374  * 
30375  */
30376
30377
30378 /**
30379  * @class Roo.bootstrap.LayoutMasonry
30380  * @extends Roo.bootstrap.Component
30381  * Bootstrap Layout Masonry class
30382  * 
30383  * @constructor
30384  * Create a new Element
30385  * @param {Object} config The config object
30386  */
30387
30388 Roo.bootstrap.LayoutMasonry = function(config){
30389     
30390     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30391     
30392     this.bricks = [];
30393     
30394     Roo.bootstrap.LayoutMasonry.register(this);
30395     
30396     this.addEvents({
30397         // raw events
30398         /**
30399          * @event layout
30400          * Fire after layout the items
30401          * @param {Roo.bootstrap.LayoutMasonry} this
30402          * @param {Roo.EventObject} e
30403          */
30404         "layout" : true
30405     });
30406     
30407 };
30408
30409 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30410     
30411     /**
30412      * @cfg {Boolean} isLayoutInstant = no animation?
30413      */   
30414     isLayoutInstant : false, // needed?
30415    
30416     /**
30417      * @cfg {Number} boxWidth  width of the columns
30418      */   
30419     boxWidth : 450,
30420     
30421       /**
30422      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30423      */   
30424     boxHeight : 0,
30425     
30426     /**
30427      * @cfg {Number} padWidth padding below box..
30428      */   
30429     padWidth : 10, 
30430     
30431     /**
30432      * @cfg {Number} gutter gutter width..
30433      */   
30434     gutter : 10,
30435     
30436      /**
30437      * @cfg {Number} maxCols maximum number of columns
30438      */   
30439     
30440     maxCols: 0,
30441     
30442     /**
30443      * @cfg {Boolean} isAutoInitial defalut true
30444      */   
30445     isAutoInitial : true, 
30446     
30447     containerWidth: 0,
30448     
30449     /**
30450      * @cfg {Boolean} isHorizontal defalut false
30451      */   
30452     isHorizontal : false, 
30453
30454     currentSize : null,
30455     
30456     tag: 'div',
30457     
30458     cls: '',
30459     
30460     bricks: null, //CompositeElement
30461     
30462     cols : 1,
30463     
30464     _isLayoutInited : false,
30465     
30466 //    isAlternative : false, // only use for vertical layout...
30467     
30468     /**
30469      * @cfg {Number} alternativePadWidth padding below box..
30470      */   
30471     alternativePadWidth : 50,
30472     
30473     selectedBrick : [],
30474     
30475     getAutoCreate : function(){
30476         
30477         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30478         
30479         var cfg = {
30480             tag: this.tag,
30481             cls: 'blog-masonary-wrapper ' + this.cls,
30482             cn : {
30483                 cls : 'mas-boxes masonary'
30484             }
30485         };
30486         
30487         return cfg;
30488     },
30489     
30490     getChildContainer: function( )
30491     {
30492         if (this.boxesEl) {
30493             return this.boxesEl;
30494         }
30495         
30496         this.boxesEl = this.el.select('.mas-boxes').first();
30497         
30498         return this.boxesEl;
30499     },
30500     
30501     
30502     initEvents : function()
30503     {
30504         var _this = this;
30505         
30506         if(this.isAutoInitial){
30507             Roo.log('hook children rendered');
30508             this.on('childrenrendered', function() {
30509                 Roo.log('children rendered');
30510                 _this.initial();
30511             } ,this);
30512         }
30513     },
30514     
30515     initial : function()
30516     {
30517         this.selectedBrick = [];
30518         
30519         this.currentSize = this.el.getBox(true);
30520         
30521         Roo.EventManager.onWindowResize(this.resize, this); 
30522
30523         if(!this.isAutoInitial){
30524             this.layout();
30525             return;
30526         }
30527         
30528         this.layout();
30529         
30530         return;
30531         //this.layout.defer(500,this);
30532         
30533     },
30534     
30535     resize : function()
30536     {
30537         var cs = this.el.getBox(true);
30538         
30539         if (
30540                 this.currentSize.width == cs.width && 
30541                 this.currentSize.x == cs.x && 
30542                 this.currentSize.height == cs.height && 
30543                 this.currentSize.y == cs.y 
30544         ) {
30545             Roo.log("no change in with or X or Y");
30546             return;
30547         }
30548         
30549         this.currentSize = cs;
30550         
30551         this.layout();
30552         
30553     },
30554     
30555     layout : function()
30556     {   
30557         this._resetLayout();
30558         
30559         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30560         
30561         this.layoutItems( isInstant );
30562       
30563         this._isLayoutInited = true;
30564         
30565         this.fireEvent('layout', this);
30566         
30567     },
30568     
30569     _resetLayout : function()
30570     {
30571         if(this.isHorizontal){
30572             this.horizontalMeasureColumns();
30573             return;
30574         }
30575         
30576         this.verticalMeasureColumns();
30577         
30578     },
30579     
30580     verticalMeasureColumns : function()
30581     {
30582         this.getContainerWidth();
30583         
30584 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30585 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30586 //            return;
30587 //        }
30588         
30589         var boxWidth = this.boxWidth + this.padWidth;
30590         
30591         if(this.containerWidth < this.boxWidth){
30592             boxWidth = this.containerWidth
30593         }
30594         
30595         var containerWidth = this.containerWidth;
30596         
30597         var cols = Math.floor(containerWidth / boxWidth);
30598         
30599         this.cols = Math.max( cols, 1 );
30600         
30601         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30602         
30603         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30604         
30605         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30606         
30607         this.colWidth = boxWidth + avail - this.padWidth;
30608         
30609         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30610         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30611     },
30612     
30613     horizontalMeasureColumns : function()
30614     {
30615         this.getContainerWidth();
30616         
30617         var boxWidth = this.boxWidth;
30618         
30619         if(this.containerWidth < boxWidth){
30620             boxWidth = this.containerWidth;
30621         }
30622         
30623         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30624         
30625         this.el.setHeight(boxWidth);
30626         
30627     },
30628     
30629     getContainerWidth : function()
30630     {
30631         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30632     },
30633     
30634     layoutItems : function( isInstant )
30635     {
30636         Roo.log(this.bricks);
30637         
30638         var items = Roo.apply([], this.bricks);
30639         
30640         if(this.isHorizontal){
30641             this._horizontalLayoutItems( items , isInstant );
30642             return;
30643         }
30644         
30645 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30646 //            this._verticalAlternativeLayoutItems( items , isInstant );
30647 //            return;
30648 //        }
30649         
30650         this._verticalLayoutItems( items , isInstant );
30651         
30652     },
30653     
30654     _verticalLayoutItems : function ( items , isInstant)
30655     {
30656         if ( !items || !items.length ) {
30657             return;
30658         }
30659         
30660         var standard = [
30661             ['xs', 'xs', 'xs', 'tall'],
30662             ['xs', 'xs', 'tall'],
30663             ['xs', 'xs', 'sm'],
30664             ['xs', 'xs', 'xs'],
30665             ['xs', 'tall'],
30666             ['xs', 'sm'],
30667             ['xs', 'xs'],
30668             ['xs'],
30669             
30670             ['sm', 'xs', 'xs'],
30671             ['sm', 'xs'],
30672             ['sm'],
30673             
30674             ['tall', 'xs', 'xs', 'xs'],
30675             ['tall', 'xs', 'xs'],
30676             ['tall', 'xs'],
30677             ['tall']
30678             
30679         ];
30680         
30681         var queue = [];
30682         
30683         var boxes = [];
30684         
30685         var box = [];
30686         
30687         Roo.each(items, function(item, k){
30688             
30689             switch (item.size) {
30690                 // these layouts take up a full box,
30691                 case 'md' :
30692                 case 'md-left' :
30693                 case 'md-right' :
30694                 case 'wide' :
30695                     
30696                     if(box.length){
30697                         boxes.push(box);
30698                         box = [];
30699                     }
30700                     
30701                     boxes.push([item]);
30702                     
30703                     break;
30704                     
30705                 case 'xs' :
30706                 case 'sm' :
30707                 case 'tall' :
30708                     
30709                     box.push(item);
30710                     
30711                     break;
30712                 default :
30713                     break;
30714                     
30715             }
30716             
30717         }, this);
30718         
30719         if(box.length){
30720             boxes.push(box);
30721             box = [];
30722         }
30723         
30724         var filterPattern = function(box, length)
30725         {
30726             if(!box.length){
30727                 return;
30728             }
30729             
30730             var match = false;
30731             
30732             var pattern = box.slice(0, length);
30733             
30734             var format = [];
30735             
30736             Roo.each(pattern, function(i){
30737                 format.push(i.size);
30738             }, this);
30739             
30740             Roo.each(standard, function(s){
30741                 
30742                 if(String(s) != String(format)){
30743                     return;
30744                 }
30745                 
30746                 match = true;
30747                 return false;
30748                 
30749             }, this);
30750             
30751             if(!match && length == 1){
30752                 return;
30753             }
30754             
30755             if(!match){
30756                 filterPattern(box, length - 1);
30757                 return;
30758             }
30759                 
30760             queue.push(pattern);
30761
30762             box = box.slice(length, box.length);
30763
30764             filterPattern(box, 4);
30765
30766             return;
30767             
30768         }
30769         
30770         Roo.each(boxes, function(box, k){
30771             
30772             if(!box.length){
30773                 return;
30774             }
30775             
30776             if(box.length == 1){
30777                 queue.push(box);
30778                 return;
30779             }
30780             
30781             filterPattern(box, 4);
30782             
30783         }, this);
30784         
30785         this._processVerticalLayoutQueue( queue, isInstant );
30786         
30787     },
30788     
30789 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30790 //    {
30791 //        if ( !items || !items.length ) {
30792 //            return;
30793 //        }
30794 //
30795 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30796 //        
30797 //    },
30798     
30799     _horizontalLayoutItems : function ( items , isInstant)
30800     {
30801         if ( !items || !items.length || items.length < 3) {
30802             return;
30803         }
30804         
30805         items.reverse();
30806         
30807         var eItems = items.slice(0, 3);
30808         
30809         items = items.slice(3, items.length);
30810         
30811         var standard = [
30812             ['xs', 'xs', 'xs', 'wide'],
30813             ['xs', 'xs', 'wide'],
30814             ['xs', 'xs', 'sm'],
30815             ['xs', 'xs', 'xs'],
30816             ['xs', 'wide'],
30817             ['xs', 'sm'],
30818             ['xs', 'xs'],
30819             ['xs'],
30820             
30821             ['sm', 'xs', 'xs'],
30822             ['sm', 'xs'],
30823             ['sm'],
30824             
30825             ['wide', 'xs', 'xs', 'xs'],
30826             ['wide', 'xs', 'xs'],
30827             ['wide', 'xs'],
30828             ['wide'],
30829             
30830             ['wide-thin']
30831         ];
30832         
30833         var queue = [];
30834         
30835         var boxes = [];
30836         
30837         var box = [];
30838         
30839         Roo.each(items, function(item, k){
30840             
30841             switch (item.size) {
30842                 case 'md' :
30843                 case 'md-left' :
30844                 case 'md-right' :
30845                 case 'tall' :
30846                     
30847                     if(box.length){
30848                         boxes.push(box);
30849                         box = [];
30850                     }
30851                     
30852                     boxes.push([item]);
30853                     
30854                     break;
30855                     
30856                 case 'xs' :
30857                 case 'sm' :
30858                 case 'wide' :
30859                 case 'wide-thin' :
30860                     
30861                     box.push(item);
30862                     
30863                     break;
30864                 default :
30865                     break;
30866                     
30867             }
30868             
30869         }, this);
30870         
30871         if(box.length){
30872             boxes.push(box);
30873             box = [];
30874         }
30875         
30876         var filterPattern = function(box, length)
30877         {
30878             if(!box.length){
30879                 return;
30880             }
30881             
30882             var match = false;
30883             
30884             var pattern = box.slice(0, length);
30885             
30886             var format = [];
30887             
30888             Roo.each(pattern, function(i){
30889                 format.push(i.size);
30890             }, this);
30891             
30892             Roo.each(standard, function(s){
30893                 
30894                 if(String(s) != String(format)){
30895                     return;
30896                 }
30897                 
30898                 match = true;
30899                 return false;
30900                 
30901             }, this);
30902             
30903             if(!match && length == 1){
30904                 return;
30905             }
30906             
30907             if(!match){
30908                 filterPattern(box, length - 1);
30909                 return;
30910             }
30911                 
30912             queue.push(pattern);
30913
30914             box = box.slice(length, box.length);
30915
30916             filterPattern(box, 4);
30917
30918             return;
30919             
30920         }
30921         
30922         Roo.each(boxes, function(box, k){
30923             
30924             if(!box.length){
30925                 return;
30926             }
30927             
30928             if(box.length == 1){
30929                 queue.push(box);
30930                 return;
30931             }
30932             
30933             filterPattern(box, 4);
30934             
30935         }, this);
30936         
30937         
30938         var prune = [];
30939         
30940         var pos = this.el.getBox(true);
30941         
30942         var minX = pos.x;
30943         
30944         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30945         
30946         var hit_end = false;
30947         
30948         Roo.each(queue, function(box){
30949             
30950             if(hit_end){
30951                 
30952                 Roo.each(box, function(b){
30953                 
30954                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30955                     b.el.hide();
30956
30957                 }, this);
30958
30959                 return;
30960             }
30961             
30962             var mx = 0;
30963             
30964             Roo.each(box, function(b){
30965                 
30966                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30967                 b.el.show();
30968
30969                 mx = Math.max(mx, b.x);
30970                 
30971             }, this);
30972             
30973             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30974             
30975             if(maxX < minX){
30976                 
30977                 Roo.each(box, function(b){
30978                 
30979                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30980                     b.el.hide();
30981                     
30982                 }, this);
30983                 
30984                 hit_end = true;
30985                 
30986                 return;
30987             }
30988             
30989             prune.push(box);
30990             
30991         }, this);
30992         
30993         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30994     },
30995     
30996     /** Sets position of item in DOM
30997     * @param {Element} item
30998     * @param {Number} x - horizontal position
30999     * @param {Number} y - vertical position
31000     * @param {Boolean} isInstant - disables transitions
31001     */
31002     _processVerticalLayoutQueue : function( queue, isInstant )
31003     {
31004         var pos = this.el.getBox(true);
31005         var x = pos.x;
31006         var y = pos.y;
31007         var maxY = [];
31008         
31009         for (var i = 0; i < this.cols; i++){
31010             maxY[i] = pos.y;
31011         }
31012         
31013         Roo.each(queue, function(box, k){
31014             
31015             var col = k % this.cols;
31016             
31017             Roo.each(box, function(b,kk){
31018                 
31019                 b.el.position('absolute');
31020                 
31021                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31022                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31023                 
31024                 if(b.size == 'md-left' || b.size == 'md-right'){
31025                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31026                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31027                 }
31028                 
31029                 b.el.setWidth(width);
31030                 b.el.setHeight(height);
31031                 // iframe?
31032                 b.el.select('iframe',true).setSize(width,height);
31033                 
31034             }, this);
31035             
31036             for (var i = 0; i < this.cols; i++){
31037                 
31038                 if(maxY[i] < maxY[col]){
31039                     col = i;
31040                     continue;
31041                 }
31042                 
31043                 col = Math.min(col, i);
31044                 
31045             }
31046             
31047             x = pos.x + col * (this.colWidth + this.padWidth);
31048             
31049             y = maxY[col];
31050             
31051             var positions = [];
31052             
31053             switch (box.length){
31054                 case 1 :
31055                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31056                     break;
31057                 case 2 :
31058                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31059                     break;
31060                 case 3 :
31061                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31062                     break;
31063                 case 4 :
31064                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31065                     break;
31066                 default :
31067                     break;
31068             }
31069             
31070             Roo.each(box, function(b,kk){
31071                 
31072                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31073                 
31074                 var sz = b.el.getSize();
31075                 
31076                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31077                 
31078             }, this);
31079             
31080         }, this);
31081         
31082         var mY = 0;
31083         
31084         for (var i = 0; i < this.cols; i++){
31085             mY = Math.max(mY, maxY[i]);
31086         }
31087         
31088         this.el.setHeight(mY - pos.y);
31089         
31090     },
31091     
31092 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31093 //    {
31094 //        var pos = this.el.getBox(true);
31095 //        var x = pos.x;
31096 //        var y = pos.y;
31097 //        var maxX = pos.right;
31098 //        
31099 //        var maxHeight = 0;
31100 //        
31101 //        Roo.each(items, function(item, k){
31102 //            
31103 //            var c = k % 2;
31104 //            
31105 //            item.el.position('absolute');
31106 //                
31107 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31108 //
31109 //            item.el.setWidth(width);
31110 //
31111 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31112 //
31113 //            item.el.setHeight(height);
31114 //            
31115 //            if(c == 0){
31116 //                item.el.setXY([x, y], isInstant ? false : true);
31117 //            } else {
31118 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31119 //            }
31120 //            
31121 //            y = y + height + this.alternativePadWidth;
31122 //            
31123 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31124 //            
31125 //        }, this);
31126 //        
31127 //        this.el.setHeight(maxHeight);
31128 //        
31129 //    },
31130     
31131     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31132     {
31133         var pos = this.el.getBox(true);
31134         
31135         var minX = pos.x;
31136         var minY = pos.y;
31137         
31138         var maxX = pos.right;
31139         
31140         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31141         
31142         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31143         
31144         Roo.each(queue, function(box, k){
31145             
31146             Roo.each(box, function(b, kk){
31147                 
31148                 b.el.position('absolute');
31149                 
31150                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31151                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31152                 
31153                 if(b.size == 'md-left' || b.size == 'md-right'){
31154                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31155                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31156                 }
31157                 
31158                 b.el.setWidth(width);
31159                 b.el.setHeight(height);
31160                 
31161             }, this);
31162             
31163             if(!box.length){
31164                 return;
31165             }
31166             
31167             var positions = [];
31168             
31169             switch (box.length){
31170                 case 1 :
31171                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31172                     break;
31173                 case 2 :
31174                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31175                     break;
31176                 case 3 :
31177                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31178                     break;
31179                 case 4 :
31180                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31181                     break;
31182                 default :
31183                     break;
31184             }
31185             
31186             Roo.each(box, function(b,kk){
31187                 
31188                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31189                 
31190                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31191                 
31192             }, this);
31193             
31194         }, this);
31195         
31196     },
31197     
31198     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31199     {
31200         Roo.each(eItems, function(b,k){
31201             
31202             b.size = (k == 0) ? 'sm' : 'xs';
31203             b.x = (k == 0) ? 2 : 1;
31204             b.y = (k == 0) ? 2 : 1;
31205             
31206             b.el.position('absolute');
31207             
31208             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31209                 
31210             b.el.setWidth(width);
31211             
31212             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31213             
31214             b.el.setHeight(height);
31215             
31216         }, this);
31217
31218         var positions = [];
31219         
31220         positions.push({
31221             x : maxX - this.unitWidth * 2 - this.gutter,
31222             y : minY
31223         });
31224         
31225         positions.push({
31226             x : maxX - this.unitWidth,
31227             y : minY + (this.unitWidth + this.gutter) * 2
31228         });
31229         
31230         positions.push({
31231             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31232             y : minY
31233         });
31234         
31235         Roo.each(eItems, function(b,k){
31236             
31237             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31238
31239         }, this);
31240         
31241     },
31242     
31243     getVerticalOneBoxColPositions : function(x, y, box)
31244     {
31245         var pos = [];
31246         
31247         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31248         
31249         if(box[0].size == 'md-left'){
31250             rand = 0;
31251         }
31252         
31253         if(box[0].size == 'md-right'){
31254             rand = 1;
31255         }
31256         
31257         pos.push({
31258             x : x + (this.unitWidth + this.gutter) * rand,
31259             y : y
31260         });
31261         
31262         return pos;
31263     },
31264     
31265     getVerticalTwoBoxColPositions : function(x, y, box)
31266     {
31267         var pos = [];
31268         
31269         if(box[0].size == 'xs'){
31270             
31271             pos.push({
31272                 x : x,
31273                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31274             });
31275
31276             pos.push({
31277                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31278                 y : y
31279             });
31280             
31281             return pos;
31282             
31283         }
31284         
31285         pos.push({
31286             x : x,
31287             y : y
31288         });
31289
31290         pos.push({
31291             x : x + (this.unitWidth + this.gutter) * 2,
31292             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31293         });
31294         
31295         return pos;
31296         
31297     },
31298     
31299     getVerticalThreeBoxColPositions : function(x, y, box)
31300     {
31301         var pos = [];
31302         
31303         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31304             
31305             pos.push({
31306                 x : x,
31307                 y : y
31308             });
31309
31310             pos.push({
31311                 x : x + (this.unitWidth + this.gutter) * 1,
31312                 y : y
31313             });
31314             
31315             pos.push({
31316                 x : x + (this.unitWidth + this.gutter) * 2,
31317                 y : y
31318             });
31319             
31320             return pos;
31321             
31322         }
31323         
31324         if(box[0].size == 'xs' && box[1].size == 'xs'){
31325             
31326             pos.push({
31327                 x : x,
31328                 y : y
31329             });
31330
31331             pos.push({
31332                 x : x,
31333                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31334             });
31335             
31336             pos.push({
31337                 x : x + (this.unitWidth + this.gutter) * 1,
31338                 y : y
31339             });
31340             
31341             return pos;
31342             
31343         }
31344         
31345         pos.push({
31346             x : x,
31347             y : y
31348         });
31349
31350         pos.push({
31351             x : x + (this.unitWidth + this.gutter) * 2,
31352             y : y
31353         });
31354
31355         pos.push({
31356             x : x + (this.unitWidth + this.gutter) * 2,
31357             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31358         });
31359             
31360         return pos;
31361         
31362     },
31363     
31364     getVerticalFourBoxColPositions : function(x, y, box)
31365     {
31366         var pos = [];
31367         
31368         if(box[0].size == 'xs'){
31369             
31370             pos.push({
31371                 x : x,
31372                 y : y
31373             });
31374
31375             pos.push({
31376                 x : x,
31377                 y : y + (this.unitHeight + this.gutter) * 1
31378             });
31379             
31380             pos.push({
31381                 x : x,
31382                 y : y + (this.unitHeight + this.gutter) * 2
31383             });
31384             
31385             pos.push({
31386                 x : x + (this.unitWidth + this.gutter) * 1,
31387                 y : y
31388             });
31389             
31390             return pos;
31391             
31392         }
31393         
31394         pos.push({
31395             x : x,
31396             y : y
31397         });
31398
31399         pos.push({
31400             x : x + (this.unitWidth + this.gutter) * 2,
31401             y : y
31402         });
31403
31404         pos.push({
31405             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31406             y : y + (this.unitHeight + this.gutter) * 1
31407         });
31408
31409         pos.push({
31410             x : x + (this.unitWidth + this.gutter) * 2,
31411             y : y + (this.unitWidth + this.gutter) * 2
31412         });
31413
31414         return pos;
31415         
31416     },
31417     
31418     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31419     {
31420         var pos = [];
31421         
31422         if(box[0].size == 'md-left'){
31423             pos.push({
31424                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31425                 y : minY
31426             });
31427             
31428             return pos;
31429         }
31430         
31431         if(box[0].size == 'md-right'){
31432             pos.push({
31433                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31434                 y : minY + (this.unitWidth + this.gutter) * 1
31435             });
31436             
31437             return pos;
31438         }
31439         
31440         var rand = Math.floor(Math.random() * (4 - box[0].y));
31441         
31442         pos.push({
31443             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31444             y : minY + (this.unitWidth + this.gutter) * rand
31445         });
31446         
31447         return pos;
31448         
31449     },
31450     
31451     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31452     {
31453         var pos = [];
31454         
31455         if(box[0].size == 'xs'){
31456             
31457             pos.push({
31458                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31459                 y : minY
31460             });
31461
31462             pos.push({
31463                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31464                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31465             });
31466             
31467             return pos;
31468             
31469         }
31470         
31471         pos.push({
31472             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31473             y : minY
31474         });
31475
31476         pos.push({
31477             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31478             y : minY + (this.unitWidth + this.gutter) * 2
31479         });
31480         
31481         return pos;
31482         
31483     },
31484     
31485     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31486     {
31487         var pos = [];
31488         
31489         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31490             
31491             pos.push({
31492                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31493                 y : minY
31494             });
31495
31496             pos.push({
31497                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31498                 y : minY + (this.unitWidth + this.gutter) * 1
31499             });
31500             
31501             pos.push({
31502                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31503                 y : minY + (this.unitWidth + this.gutter) * 2
31504             });
31505             
31506             return pos;
31507             
31508         }
31509         
31510         if(box[0].size == 'xs' && box[1].size == 'xs'){
31511             
31512             pos.push({
31513                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31514                 y : minY
31515             });
31516
31517             pos.push({
31518                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31519                 y : minY
31520             });
31521             
31522             pos.push({
31523                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31524                 y : minY + (this.unitWidth + this.gutter) * 1
31525             });
31526             
31527             return pos;
31528             
31529         }
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[1].x - this.gutter * (box[1].x - 1),
31538             y : minY + (this.unitWidth + this.gutter) * 2
31539         });
31540
31541         pos.push({
31542             x : maxX - 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 + (this.unitWidth + this.gutter) * 2
31544         });
31545             
31546         return pos;
31547         
31548     },
31549     
31550     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31551     {
31552         var pos = [];
31553         
31554         if(box[0].size == 'xs'){
31555             
31556             pos.push({
31557                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31558                 y : minY
31559             });
31560
31561             pos.push({
31562                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31563                 y : minY
31564             });
31565             
31566             pos.push({
31567                 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),
31568                 y : minY
31569             });
31570             
31571             pos.push({
31572                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31573                 y : minY + (this.unitWidth + this.gutter) * 1
31574             });
31575             
31576             return pos;
31577             
31578         }
31579         
31580         pos.push({
31581             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31582             y : minY
31583         });
31584         
31585         pos.push({
31586             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31587             y : minY + (this.unitWidth + this.gutter) * 2
31588         });
31589         
31590         pos.push({
31591             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31592             y : minY + (this.unitWidth + this.gutter) * 2
31593         });
31594         
31595         pos.push({
31596             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),
31597             y : minY + (this.unitWidth + this.gutter) * 2
31598         });
31599
31600         return pos;
31601         
31602     },
31603     
31604     /**
31605     * remove a Masonry Brick
31606     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31607     */
31608     removeBrick : function(brick_id)
31609     {
31610         if (!brick_id) {
31611             return;
31612         }
31613         
31614         for (var i = 0; i<this.bricks.length; i++) {
31615             if (this.bricks[i].id == brick_id) {
31616                 this.bricks.splice(i,1);
31617                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31618                 this.initial();
31619             }
31620         }
31621     },
31622     
31623     /**
31624     * adds a Masonry Brick
31625     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31626     */
31627     addBrick : function(cfg)
31628     {
31629         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31630         //this.register(cn);
31631         cn.parentId = this.id;
31632         cn.onRender(this.el, null);
31633         return cn;
31634     },
31635     
31636     /**
31637     * register a Masonry Brick
31638     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31639     */
31640     
31641     register : function(brick)
31642     {
31643         this.bricks.push(brick);
31644         brick.masonryId = this.id;
31645     },
31646     
31647     /**
31648     * clear all the Masonry Brick
31649     */
31650     clearAll : function()
31651     {
31652         this.bricks = [];
31653         //this.getChildContainer().dom.innerHTML = "";
31654         this.el.dom.innerHTML = '';
31655     },
31656     
31657     getSelected : function()
31658     {
31659         if (!this.selectedBrick) {
31660             return false;
31661         }
31662         
31663         return this.selectedBrick;
31664     }
31665 });
31666
31667 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31668     
31669     groups: {},
31670      /**
31671     * register a Masonry Layout
31672     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31673     */
31674     
31675     register : function(layout)
31676     {
31677         this.groups[layout.id] = layout;
31678     },
31679     /**
31680     * fetch a  Masonry Layout based on the masonry layout ID
31681     * @param {string} the masonry layout to add
31682     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31683     */
31684     
31685     get: function(layout_id) {
31686         if (typeof(this.groups[layout_id]) == 'undefined') {
31687             return false;
31688         }
31689         return this.groups[layout_id] ;
31690     }
31691     
31692     
31693     
31694 });
31695
31696  
31697
31698  /**
31699  *
31700  * This is based on 
31701  * http://masonry.desandro.com
31702  *
31703  * The idea is to render all the bricks based on vertical width...
31704  *
31705  * The original code extends 'outlayer' - we might need to use that....
31706  * 
31707  */
31708
31709
31710 /**
31711  * @class Roo.bootstrap.LayoutMasonryAuto
31712  * @extends Roo.bootstrap.Component
31713  * Bootstrap Layout Masonry class
31714  * 
31715  * @constructor
31716  * Create a new Element
31717  * @param {Object} config The config object
31718  */
31719
31720 Roo.bootstrap.LayoutMasonryAuto = function(config){
31721     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31722 };
31723
31724 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31725     
31726       /**
31727      * @cfg {Boolean} isFitWidth  - resize the width..
31728      */   
31729     isFitWidth : false,  // options..
31730     /**
31731      * @cfg {Boolean} isOriginLeft = left align?
31732      */   
31733     isOriginLeft : true,
31734     /**
31735      * @cfg {Boolean} isOriginTop = top align?
31736      */   
31737     isOriginTop : false,
31738     /**
31739      * @cfg {Boolean} isLayoutInstant = no animation?
31740      */   
31741     isLayoutInstant : false, // needed?
31742     /**
31743      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31744      */   
31745     isResizingContainer : true,
31746     /**
31747      * @cfg {Number} columnWidth  width of the columns 
31748      */   
31749     
31750     columnWidth : 0,
31751     
31752     /**
31753      * @cfg {Number} maxCols maximum number of columns
31754      */   
31755     
31756     maxCols: 0,
31757     /**
31758      * @cfg {Number} padHeight padding below box..
31759      */   
31760     
31761     padHeight : 10, 
31762     
31763     /**
31764      * @cfg {Boolean} isAutoInitial defalut true
31765      */   
31766     
31767     isAutoInitial : true, 
31768     
31769     // private?
31770     gutter : 0,
31771     
31772     containerWidth: 0,
31773     initialColumnWidth : 0,
31774     currentSize : null,
31775     
31776     colYs : null, // array.
31777     maxY : 0,
31778     padWidth: 10,
31779     
31780     
31781     tag: 'div',
31782     cls: '',
31783     bricks: null, //CompositeElement
31784     cols : 0, // array?
31785     // element : null, // wrapped now this.el
31786     _isLayoutInited : null, 
31787     
31788     
31789     getAutoCreate : function(){
31790         
31791         var cfg = {
31792             tag: this.tag,
31793             cls: 'blog-masonary-wrapper ' + this.cls,
31794             cn : {
31795                 cls : 'mas-boxes masonary'
31796             }
31797         };
31798         
31799         return cfg;
31800     },
31801     
31802     getChildContainer: function( )
31803     {
31804         if (this.boxesEl) {
31805             return this.boxesEl;
31806         }
31807         
31808         this.boxesEl = this.el.select('.mas-boxes').first();
31809         
31810         return this.boxesEl;
31811     },
31812     
31813     
31814     initEvents : function()
31815     {
31816         var _this = this;
31817         
31818         if(this.isAutoInitial){
31819             Roo.log('hook children rendered');
31820             this.on('childrenrendered', function() {
31821                 Roo.log('children rendered');
31822                 _this.initial();
31823             } ,this);
31824         }
31825         
31826     },
31827     
31828     initial : function()
31829     {
31830         this.reloadItems();
31831
31832         this.currentSize = this.el.getBox(true);
31833
31834         /// was window resize... - let's see if this works..
31835         Roo.EventManager.onWindowResize(this.resize, this); 
31836
31837         if(!this.isAutoInitial){
31838             this.layout();
31839             return;
31840         }
31841         
31842         this.layout.defer(500,this);
31843     },
31844     
31845     reloadItems: function()
31846     {
31847         this.bricks = this.el.select('.masonry-brick', true);
31848         
31849         this.bricks.each(function(b) {
31850             //Roo.log(b.getSize());
31851             if (!b.attr('originalwidth')) {
31852                 b.attr('originalwidth',  b.getSize().width);
31853             }
31854             
31855         });
31856         
31857         Roo.log(this.bricks.elements.length);
31858     },
31859     
31860     resize : function()
31861     {
31862         Roo.log('resize');
31863         var cs = this.el.getBox(true);
31864         
31865         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31866             Roo.log("no change in with or X");
31867             return;
31868         }
31869         this.currentSize = cs;
31870         this.layout();
31871     },
31872     
31873     layout : function()
31874     {
31875          Roo.log('layout');
31876         this._resetLayout();
31877         //this._manageStamps();
31878       
31879         // don't animate first layout
31880         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31881         this.layoutItems( isInstant );
31882       
31883         // flag for initalized
31884         this._isLayoutInited = true;
31885     },
31886     
31887     layoutItems : function( isInstant )
31888     {
31889         //var items = this._getItemsForLayout( this.items );
31890         // original code supports filtering layout items.. we just ignore it..
31891         
31892         this._layoutItems( this.bricks , isInstant );
31893       
31894         this._postLayout();
31895     },
31896     _layoutItems : function ( items , isInstant)
31897     {
31898        //this.fireEvent( 'layout', this, items );
31899     
31900
31901         if ( !items || !items.elements.length ) {
31902           // no items, emit event with empty array
31903             return;
31904         }
31905
31906         var queue = [];
31907         items.each(function(item) {
31908             Roo.log("layout item");
31909             Roo.log(item);
31910             // get x/y object from method
31911             var position = this._getItemLayoutPosition( item );
31912             // enqueue
31913             position.item = item;
31914             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31915             queue.push( position );
31916         }, this);
31917       
31918         this._processLayoutQueue( queue );
31919     },
31920     /** Sets position of item in DOM
31921     * @param {Element} item
31922     * @param {Number} x - horizontal position
31923     * @param {Number} y - vertical position
31924     * @param {Boolean} isInstant - disables transitions
31925     */
31926     _processLayoutQueue : function( queue )
31927     {
31928         for ( var i=0, len = queue.length; i < len; i++ ) {
31929             var obj = queue[i];
31930             obj.item.position('absolute');
31931             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31932         }
31933     },
31934       
31935     
31936     /**
31937     * Any logic you want to do after each layout,
31938     * i.e. size the container
31939     */
31940     _postLayout : function()
31941     {
31942         this.resizeContainer();
31943     },
31944     
31945     resizeContainer : function()
31946     {
31947         if ( !this.isResizingContainer ) {
31948             return;
31949         }
31950         var size = this._getContainerSize();
31951         if ( size ) {
31952             this.el.setSize(size.width,size.height);
31953             this.boxesEl.setSize(size.width,size.height);
31954         }
31955     },
31956     
31957     
31958     
31959     _resetLayout : function()
31960     {
31961         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31962         this.colWidth = this.el.getWidth();
31963         //this.gutter = this.el.getWidth(); 
31964         
31965         this.measureColumns();
31966
31967         // reset column Y
31968         var i = this.cols;
31969         this.colYs = [];
31970         while (i--) {
31971             this.colYs.push( 0 );
31972         }
31973     
31974         this.maxY = 0;
31975     },
31976
31977     measureColumns : function()
31978     {
31979         this.getContainerWidth();
31980       // if columnWidth is 0, default to outerWidth of first item
31981         if ( !this.columnWidth ) {
31982             var firstItem = this.bricks.first();
31983             Roo.log(firstItem);
31984             this.columnWidth  = this.containerWidth;
31985             if (firstItem && firstItem.attr('originalwidth') ) {
31986                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31987             }
31988             // columnWidth fall back to item of first element
31989             Roo.log("set column width?");
31990                         this.initialColumnWidth = this.columnWidth  ;
31991
31992             // if first elem has no width, default to size of container
31993             
31994         }
31995         
31996         
31997         if (this.initialColumnWidth) {
31998             this.columnWidth = this.initialColumnWidth;
31999         }
32000         
32001         
32002             
32003         // column width is fixed at the top - however if container width get's smaller we should
32004         // reduce it...
32005         
32006         // this bit calcs how man columns..
32007             
32008         var columnWidth = this.columnWidth += this.gutter;
32009       
32010         // calculate columns
32011         var containerWidth = this.containerWidth + this.gutter;
32012         
32013         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32014         // fix rounding errors, typically with gutters
32015         var excess = columnWidth - containerWidth % columnWidth;
32016         
32017         
32018         // if overshoot is less than a pixel, round up, otherwise floor it
32019         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32020         cols = Math[ mathMethod ]( cols );
32021         this.cols = Math.max( cols, 1 );
32022         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32023         
32024          // padding positioning..
32025         var totalColWidth = this.cols * this.columnWidth;
32026         var padavail = this.containerWidth - totalColWidth;
32027         // so for 2 columns - we need 3 'pads'
32028         
32029         var padNeeded = (1+this.cols) * this.padWidth;
32030         
32031         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32032         
32033         this.columnWidth += padExtra
32034         //this.padWidth = Math.floor(padavail /  ( this.cols));
32035         
32036         // adjust colum width so that padding is fixed??
32037         
32038         // we have 3 columns ... total = width * 3
32039         // we have X left over... that should be used by 
32040         
32041         //if (this.expandC) {
32042             
32043         //}
32044         
32045         
32046         
32047     },
32048     
32049     getContainerWidth : function()
32050     {
32051        /* // container is parent if fit width
32052         var container = this.isFitWidth ? this.element.parentNode : this.element;
32053         // check that this.size and size are there
32054         // IE8 triggers resize on body size change, so they might not be
32055         
32056         var size = getSize( container );  //FIXME
32057         this.containerWidth = size && size.innerWidth; //FIXME
32058         */
32059          
32060         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32061         
32062     },
32063     
32064     _getItemLayoutPosition : function( item )  // what is item?
32065     {
32066         // we resize the item to our columnWidth..
32067       
32068         item.setWidth(this.columnWidth);
32069         item.autoBoxAdjust  = false;
32070         
32071         var sz = item.getSize();
32072  
32073         // how many columns does this brick span
32074         var remainder = this.containerWidth % this.columnWidth;
32075         
32076         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32077         // round if off by 1 pixel, otherwise use ceil
32078         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32079         colSpan = Math.min( colSpan, this.cols );
32080         
32081         // normally this should be '1' as we dont' currently allow multi width columns..
32082         
32083         var colGroup = this._getColGroup( colSpan );
32084         // get the minimum Y value from the columns
32085         var minimumY = Math.min.apply( Math, colGroup );
32086         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32087         
32088         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32089          
32090         // position the brick
32091         var position = {
32092             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32093             y: this.currentSize.y + minimumY + this.padHeight
32094         };
32095         
32096         Roo.log(position);
32097         // apply setHeight to necessary columns
32098         var setHeight = minimumY + sz.height + this.padHeight;
32099         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32100         
32101         var setSpan = this.cols + 1 - colGroup.length;
32102         for ( var i = 0; i < setSpan; i++ ) {
32103           this.colYs[ shortColIndex + i ] = setHeight ;
32104         }
32105       
32106         return position;
32107     },
32108     
32109     /**
32110      * @param {Number} colSpan - number of columns the element spans
32111      * @returns {Array} colGroup
32112      */
32113     _getColGroup : function( colSpan )
32114     {
32115         if ( colSpan < 2 ) {
32116           // if brick spans only one column, use all the column Ys
32117           return this.colYs;
32118         }
32119       
32120         var colGroup = [];
32121         // how many different places could this brick fit horizontally
32122         var groupCount = this.cols + 1 - colSpan;
32123         // for each group potential horizontal position
32124         for ( var i = 0; i < groupCount; i++ ) {
32125           // make an array of colY values for that one group
32126           var groupColYs = this.colYs.slice( i, i + colSpan );
32127           // and get the max value of the array
32128           colGroup[i] = Math.max.apply( Math, groupColYs );
32129         }
32130         return colGroup;
32131     },
32132     /*
32133     _manageStamp : function( stamp )
32134     {
32135         var stampSize =  stamp.getSize();
32136         var offset = stamp.getBox();
32137         // get the columns that this stamp affects
32138         var firstX = this.isOriginLeft ? offset.x : offset.right;
32139         var lastX = firstX + stampSize.width;
32140         var firstCol = Math.floor( firstX / this.columnWidth );
32141         firstCol = Math.max( 0, firstCol );
32142         
32143         var lastCol = Math.floor( lastX / this.columnWidth );
32144         // lastCol should not go over if multiple of columnWidth #425
32145         lastCol -= lastX % this.columnWidth ? 0 : 1;
32146         lastCol = Math.min( this.cols - 1, lastCol );
32147         
32148         // set colYs to bottom of the stamp
32149         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32150             stampSize.height;
32151             
32152         for ( var i = firstCol; i <= lastCol; i++ ) {
32153           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32154         }
32155     },
32156     */
32157     
32158     _getContainerSize : function()
32159     {
32160         this.maxY = Math.max.apply( Math, this.colYs );
32161         var size = {
32162             height: this.maxY
32163         };
32164       
32165         if ( this.isFitWidth ) {
32166             size.width = this._getContainerFitWidth();
32167         }
32168       
32169         return size;
32170     },
32171     
32172     _getContainerFitWidth : function()
32173     {
32174         var unusedCols = 0;
32175         // count unused columns
32176         var i = this.cols;
32177         while ( --i ) {
32178           if ( this.colYs[i] !== 0 ) {
32179             break;
32180           }
32181           unusedCols++;
32182         }
32183         // fit container to columns that have been used
32184         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32185     },
32186     
32187     needsResizeLayout : function()
32188     {
32189         var previousWidth = this.containerWidth;
32190         this.getContainerWidth();
32191         return previousWidth !== this.containerWidth;
32192     }
32193  
32194 });
32195
32196  
32197
32198  /*
32199  * - LGPL
32200  *
32201  * element
32202  * 
32203  */
32204
32205 /**
32206  * @class Roo.bootstrap.MasonryBrick
32207  * @extends Roo.bootstrap.Component
32208  * Bootstrap MasonryBrick class
32209  * 
32210  * @constructor
32211  * Create a new MasonryBrick
32212  * @param {Object} config The config object
32213  */
32214
32215 Roo.bootstrap.MasonryBrick = function(config){
32216     
32217     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32218     
32219     Roo.bootstrap.MasonryBrick.register(this);
32220     
32221     this.addEvents({
32222         // raw events
32223         /**
32224          * @event click
32225          * When a MasonryBrick is clcik
32226          * @param {Roo.bootstrap.MasonryBrick} this
32227          * @param {Roo.EventObject} e
32228          */
32229         "click" : true
32230     });
32231 };
32232
32233 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32234     
32235     /**
32236      * @cfg {String} title
32237      */   
32238     title : '',
32239     /**
32240      * @cfg {String} html
32241      */   
32242     html : '',
32243     /**
32244      * @cfg {String} bgimage
32245      */   
32246     bgimage : '',
32247     /**
32248      * @cfg {String} videourl
32249      */   
32250     videourl : '',
32251     /**
32252      * @cfg {String} cls
32253      */   
32254     cls : '',
32255     /**
32256      * @cfg {String} href
32257      */   
32258     href : '',
32259     /**
32260      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32261      */   
32262     size : 'xs',
32263     
32264     /**
32265      * @cfg {String} placetitle (center|bottom)
32266      */   
32267     placetitle : '',
32268     
32269     /**
32270      * @cfg {Boolean} isFitContainer defalut true
32271      */   
32272     isFitContainer : true, 
32273     
32274     /**
32275      * @cfg {Boolean} preventDefault defalut false
32276      */   
32277     preventDefault : false, 
32278     
32279     /**
32280      * @cfg {Boolean} inverse defalut false
32281      */   
32282     maskInverse : false, 
32283     
32284     getAutoCreate : function()
32285     {
32286         if(!this.isFitContainer){
32287             return this.getSplitAutoCreate();
32288         }
32289         
32290         var cls = 'masonry-brick masonry-brick-full';
32291         
32292         if(this.href.length){
32293             cls += ' masonry-brick-link';
32294         }
32295         
32296         if(this.bgimage.length){
32297             cls += ' masonry-brick-image';
32298         }
32299         
32300         if(this.maskInverse){
32301             cls += ' mask-inverse';
32302         }
32303         
32304         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32305             cls += ' enable-mask';
32306         }
32307         
32308         if(this.size){
32309             cls += ' masonry-' + this.size + '-brick';
32310         }
32311         
32312         if(this.placetitle.length){
32313             
32314             switch (this.placetitle) {
32315                 case 'center' :
32316                     cls += ' masonry-center-title';
32317                     break;
32318                 case 'bottom' :
32319                     cls += ' masonry-bottom-title';
32320                     break;
32321                 default:
32322                     break;
32323             }
32324             
32325         } else {
32326             if(!this.html.length && !this.bgimage.length){
32327                 cls += ' masonry-center-title';
32328             }
32329
32330             if(!this.html.length && this.bgimage.length){
32331                 cls += ' masonry-bottom-title';
32332             }
32333         }
32334         
32335         if(this.cls){
32336             cls += ' ' + this.cls;
32337         }
32338         
32339         var cfg = {
32340             tag: (this.href.length) ? 'a' : 'div',
32341             cls: cls,
32342             cn: [
32343                 {
32344                     tag: 'div',
32345                     cls: 'masonry-brick-mask'
32346                 },
32347                 {
32348                     tag: 'div',
32349                     cls: 'masonry-brick-paragraph',
32350                     cn: []
32351                 }
32352             ]
32353         };
32354         
32355         if(this.href.length){
32356             cfg.href = this.href;
32357         }
32358         
32359         var cn = cfg.cn[1].cn;
32360         
32361         if(this.title.length){
32362             cn.push({
32363                 tag: 'h4',
32364                 cls: 'masonry-brick-title',
32365                 html: this.title
32366             });
32367         }
32368         
32369         if(this.html.length){
32370             cn.push({
32371                 tag: 'p',
32372                 cls: 'masonry-brick-text',
32373                 html: this.html
32374             });
32375         }
32376         
32377         if (!this.title.length && !this.html.length) {
32378             cfg.cn[1].cls += ' hide';
32379         }
32380         
32381         if(this.bgimage.length){
32382             cfg.cn.push({
32383                 tag: 'img',
32384                 cls: 'masonry-brick-image-view',
32385                 src: this.bgimage
32386             });
32387         }
32388         
32389         if(this.videourl.length){
32390             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32391             // youtube support only?
32392             cfg.cn.push({
32393                 tag: 'iframe',
32394                 cls: 'masonry-brick-image-view',
32395                 src: vurl,
32396                 frameborder : 0,
32397                 allowfullscreen : true
32398             });
32399         }
32400         
32401         return cfg;
32402         
32403     },
32404     
32405     getSplitAutoCreate : function()
32406     {
32407         var cls = 'masonry-brick masonry-brick-split';
32408         
32409         if(this.href.length){
32410             cls += ' masonry-brick-link';
32411         }
32412         
32413         if(this.bgimage.length){
32414             cls += ' masonry-brick-image';
32415         }
32416         
32417         if(this.size){
32418             cls += ' masonry-' + this.size + '-brick';
32419         }
32420         
32421         switch (this.placetitle) {
32422             case 'center' :
32423                 cls += ' masonry-center-title';
32424                 break;
32425             case 'bottom' :
32426                 cls += ' masonry-bottom-title';
32427                 break;
32428             default:
32429                 if(!this.bgimage.length){
32430                     cls += ' masonry-center-title';
32431                 }
32432
32433                 if(this.bgimage.length){
32434                     cls += ' masonry-bottom-title';
32435                 }
32436                 break;
32437         }
32438         
32439         if(this.cls){
32440             cls += ' ' + this.cls;
32441         }
32442         
32443         var cfg = {
32444             tag: (this.href.length) ? 'a' : 'div',
32445             cls: cls,
32446             cn: [
32447                 {
32448                     tag: 'div',
32449                     cls: 'masonry-brick-split-head',
32450                     cn: [
32451                         {
32452                             tag: 'div',
32453                             cls: 'masonry-brick-paragraph',
32454                             cn: []
32455                         }
32456                     ]
32457                 },
32458                 {
32459                     tag: 'div',
32460                     cls: 'masonry-brick-split-body',
32461                     cn: []
32462                 }
32463             ]
32464         };
32465         
32466         if(this.href.length){
32467             cfg.href = this.href;
32468         }
32469         
32470         if(this.title.length){
32471             cfg.cn[0].cn[0].cn.push({
32472                 tag: 'h4',
32473                 cls: 'masonry-brick-title',
32474                 html: this.title
32475             });
32476         }
32477         
32478         if(this.html.length){
32479             cfg.cn[1].cn.push({
32480                 tag: 'p',
32481                 cls: 'masonry-brick-text',
32482                 html: this.html
32483             });
32484         }
32485
32486         if(this.bgimage.length){
32487             cfg.cn[0].cn.push({
32488                 tag: 'img',
32489                 cls: 'masonry-brick-image-view',
32490                 src: this.bgimage
32491             });
32492         }
32493         
32494         if(this.videourl.length){
32495             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32496             // youtube support only?
32497             cfg.cn[0].cn.cn.push({
32498                 tag: 'iframe',
32499                 cls: 'masonry-brick-image-view',
32500                 src: vurl,
32501                 frameborder : 0,
32502                 allowfullscreen : true
32503             });
32504         }
32505         
32506         return cfg;
32507     },
32508     
32509     initEvents: function() 
32510     {
32511         switch (this.size) {
32512             case 'xs' :
32513                 this.x = 1;
32514                 this.y = 1;
32515                 break;
32516             case 'sm' :
32517                 this.x = 2;
32518                 this.y = 2;
32519                 break;
32520             case 'md' :
32521             case 'md-left' :
32522             case 'md-right' :
32523                 this.x = 3;
32524                 this.y = 3;
32525                 break;
32526             case 'tall' :
32527                 this.x = 2;
32528                 this.y = 3;
32529                 break;
32530             case 'wide' :
32531                 this.x = 3;
32532                 this.y = 2;
32533                 break;
32534             case 'wide-thin' :
32535                 this.x = 3;
32536                 this.y = 1;
32537                 break;
32538                         
32539             default :
32540                 break;
32541         }
32542         
32543         if(Roo.isTouch){
32544             this.el.on('touchstart', this.onTouchStart, this);
32545             this.el.on('touchmove', this.onTouchMove, this);
32546             this.el.on('touchend', this.onTouchEnd, this);
32547             this.el.on('contextmenu', this.onContextMenu, this);
32548         } else {
32549             this.el.on('mouseenter'  ,this.enter, this);
32550             this.el.on('mouseleave', this.leave, this);
32551             this.el.on('click', this.onClick, this);
32552         }
32553         
32554         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32555             this.parent().bricks.push(this);   
32556         }
32557         
32558     },
32559     
32560     onClick: function(e, el)
32561     {
32562         var time = this.endTimer - this.startTimer;
32563         // Roo.log(e.preventDefault());
32564         if(Roo.isTouch){
32565             if(time > 1000){
32566                 e.preventDefault();
32567                 return;
32568             }
32569         }
32570         
32571         if(!this.preventDefault){
32572             return;
32573         }
32574         
32575         e.preventDefault();
32576         
32577         if (this.activcClass != '') {
32578             this.selectBrick();
32579         }
32580         
32581         this.fireEvent('click', this);
32582     },
32583     
32584     enter: function(e, el)
32585     {
32586         e.preventDefault();
32587         
32588         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32589             return;
32590         }
32591         
32592         if(this.bgimage.length && this.html.length){
32593             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32594         }
32595     },
32596     
32597     leave: function(e, el)
32598     {
32599         e.preventDefault();
32600         
32601         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32602             return;
32603         }
32604         
32605         if(this.bgimage.length && this.html.length){
32606             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32607         }
32608     },
32609     
32610     onTouchStart: function(e, el)
32611     {
32612 //        e.preventDefault();
32613         
32614         this.touchmoved = false;
32615         
32616         if(!this.isFitContainer){
32617             return;
32618         }
32619         
32620         if(!this.bgimage.length || !this.html.length){
32621             return;
32622         }
32623         
32624         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32625         
32626         this.timer = new Date().getTime();
32627         
32628     },
32629     
32630     onTouchMove: function(e, el)
32631     {
32632         this.touchmoved = true;
32633     },
32634     
32635     onContextMenu : function(e,el)
32636     {
32637         e.preventDefault();
32638         e.stopPropagation();
32639         return false;
32640     },
32641     
32642     onTouchEnd: function(e, el)
32643     {
32644 //        e.preventDefault();
32645         
32646         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32647         
32648             this.leave(e,el);
32649             
32650             return;
32651         }
32652         
32653         if(!this.bgimage.length || !this.html.length){
32654             
32655             if(this.href.length){
32656                 window.location.href = this.href;
32657             }
32658             
32659             return;
32660         }
32661         
32662         if(!this.isFitContainer){
32663             return;
32664         }
32665         
32666         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32667         
32668         window.location.href = this.href;
32669     },
32670     
32671     //selection on single brick only
32672     selectBrick : function() {
32673         
32674         if (!this.parentId) {
32675             return;
32676         }
32677         
32678         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32679         var index = m.selectedBrick.indexOf(this.id);
32680         
32681         if ( index > -1) {
32682             m.selectedBrick.splice(index,1);
32683             this.el.removeClass(this.activeClass);
32684             return;
32685         }
32686         
32687         for(var i = 0; i < m.selectedBrick.length; i++) {
32688             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32689             b.el.removeClass(b.activeClass);
32690         }
32691         
32692         m.selectedBrick = [];
32693         
32694         m.selectedBrick.push(this.id);
32695         this.el.addClass(this.activeClass);
32696         return;
32697     }
32698     
32699 });
32700
32701 Roo.apply(Roo.bootstrap.MasonryBrick, {
32702     
32703     //groups: {},
32704     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32705      /**
32706     * register a Masonry Brick
32707     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32708     */
32709     
32710     register : function(brick)
32711     {
32712         //this.groups[brick.id] = brick;
32713         this.groups.add(brick.id, brick);
32714     },
32715     /**
32716     * fetch a  masonry brick based on the masonry brick ID
32717     * @param {string} the masonry brick to add
32718     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32719     */
32720     
32721     get: function(brick_id) 
32722     {
32723         // if (typeof(this.groups[brick_id]) == 'undefined') {
32724         //     return false;
32725         // }
32726         // return this.groups[brick_id] ;
32727         
32728         if(this.groups.key(brick_id)) {
32729             return this.groups.key(brick_id);
32730         }
32731         
32732         return false;
32733     }
32734     
32735     
32736     
32737 });
32738
32739  /*
32740  * - LGPL
32741  *
32742  * element
32743  * 
32744  */
32745
32746 /**
32747  * @class Roo.bootstrap.Brick
32748  * @extends Roo.bootstrap.Component
32749  * Bootstrap Brick class
32750  * 
32751  * @constructor
32752  * Create a new Brick
32753  * @param {Object} config The config object
32754  */
32755
32756 Roo.bootstrap.Brick = function(config){
32757     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32758     
32759     this.addEvents({
32760         // raw events
32761         /**
32762          * @event click
32763          * When a Brick is click
32764          * @param {Roo.bootstrap.Brick} this
32765          * @param {Roo.EventObject} e
32766          */
32767         "click" : true
32768     });
32769 };
32770
32771 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32772     
32773     /**
32774      * @cfg {String} title
32775      */   
32776     title : '',
32777     /**
32778      * @cfg {String} html
32779      */   
32780     html : '',
32781     /**
32782      * @cfg {String} bgimage
32783      */   
32784     bgimage : '',
32785     /**
32786      * @cfg {String} cls
32787      */   
32788     cls : '',
32789     /**
32790      * @cfg {String} href
32791      */   
32792     href : '',
32793     /**
32794      * @cfg {String} video
32795      */   
32796     video : '',
32797     /**
32798      * @cfg {Boolean} square
32799      */   
32800     square : true,
32801     
32802     getAutoCreate : function()
32803     {
32804         var cls = 'roo-brick';
32805         
32806         if(this.href.length){
32807             cls += ' roo-brick-link';
32808         }
32809         
32810         if(this.bgimage.length){
32811             cls += ' roo-brick-image';
32812         }
32813         
32814         if(!this.html.length && !this.bgimage.length){
32815             cls += ' roo-brick-center-title';
32816         }
32817         
32818         if(!this.html.length && this.bgimage.length){
32819             cls += ' roo-brick-bottom-title';
32820         }
32821         
32822         if(this.cls){
32823             cls += ' ' + this.cls;
32824         }
32825         
32826         var cfg = {
32827             tag: (this.href.length) ? 'a' : 'div',
32828             cls: cls,
32829             cn: [
32830                 {
32831                     tag: 'div',
32832                     cls: 'roo-brick-paragraph',
32833                     cn: []
32834                 }
32835             ]
32836         };
32837         
32838         if(this.href.length){
32839             cfg.href = this.href;
32840         }
32841         
32842         var cn = cfg.cn[0].cn;
32843         
32844         if(this.title.length){
32845             cn.push({
32846                 tag: 'h4',
32847                 cls: 'roo-brick-title',
32848                 html: this.title
32849             });
32850         }
32851         
32852         if(this.html.length){
32853             cn.push({
32854                 tag: 'p',
32855                 cls: 'roo-brick-text',
32856                 html: this.html
32857             });
32858         } else {
32859             cn.cls += ' hide';
32860         }
32861         
32862         if(this.bgimage.length){
32863             cfg.cn.push({
32864                 tag: 'img',
32865                 cls: 'roo-brick-image-view',
32866                 src: this.bgimage
32867             });
32868         }
32869         
32870         return cfg;
32871     },
32872     
32873     initEvents: function() 
32874     {
32875         if(this.title.length || this.html.length){
32876             this.el.on('mouseenter'  ,this.enter, this);
32877             this.el.on('mouseleave', this.leave, this);
32878         }
32879         
32880         Roo.EventManager.onWindowResize(this.resize, this); 
32881         
32882         if(this.bgimage.length){
32883             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32884             this.imageEl.on('load', this.onImageLoad, this);
32885             return;
32886         }
32887         
32888         this.resize();
32889     },
32890     
32891     onImageLoad : function()
32892     {
32893         this.resize();
32894     },
32895     
32896     resize : function()
32897     {
32898         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32899         
32900         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32901         
32902         if(this.bgimage.length){
32903             var image = this.el.select('.roo-brick-image-view', true).first();
32904             
32905             image.setWidth(paragraph.getWidth());
32906             
32907             if(this.square){
32908                 image.setHeight(paragraph.getWidth());
32909             }
32910             
32911             this.el.setHeight(image.getHeight());
32912             paragraph.setHeight(image.getHeight());
32913             
32914         }
32915         
32916     },
32917     
32918     enter: function(e, el)
32919     {
32920         e.preventDefault();
32921         
32922         if(this.bgimage.length){
32923             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32924             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32925         }
32926     },
32927     
32928     leave: function(e, el)
32929     {
32930         e.preventDefault();
32931         
32932         if(this.bgimage.length){
32933             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32934             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32935         }
32936     }
32937     
32938 });
32939
32940  
32941
32942  /*
32943  * - LGPL
32944  *
32945  * Input
32946  * 
32947  */
32948
32949 /**
32950  * @class Roo.bootstrap.NumberField
32951  * @extends Roo.bootstrap.Input
32952  * Bootstrap NumberField class
32953  * 
32954  * 
32955  * 
32956  * 
32957  * @constructor
32958  * Create a new NumberField
32959  * @param {Object} config The config object
32960  */
32961
32962 Roo.bootstrap.NumberField = function(config){
32963     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32964 };
32965
32966 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32967     
32968     /**
32969      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32970      */
32971     allowDecimals : true,
32972     /**
32973      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32974      */
32975     decimalSeparator : ".",
32976     /**
32977      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32978      */
32979     decimalPrecision : 2,
32980     /**
32981      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32982      */
32983     allowNegative : true,
32984     /**
32985      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32986      */
32987     minValue : Number.NEGATIVE_INFINITY,
32988     /**
32989      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32990      */
32991     maxValue : Number.MAX_VALUE,
32992     /**
32993      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32994      */
32995     minText : "The minimum value for this field is {0}",
32996     /**
32997      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32998      */
32999     maxText : "The maximum value for this field is {0}",
33000     /**
33001      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33002      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33003      */
33004     nanText : "{0} is not a valid number",
33005     /**
33006      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33007      */
33008     castInt : true,
33009
33010     // private
33011     initEvents : function()
33012     {   
33013         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33014         
33015         var allowed = "0123456789";
33016         
33017         if(this.allowDecimals){
33018             allowed += this.decimalSeparator;
33019         }
33020         
33021         if(this.allowNegative){
33022             allowed += "-";
33023         }
33024         
33025         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33026         
33027         var keyPress = function(e){
33028             
33029             var k = e.getKey();
33030             
33031             var c = e.getCharCode();
33032             
33033             if(
33034                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33035                     allowed.indexOf(String.fromCharCode(c)) === -1
33036             ){
33037                 e.stopEvent();
33038                 return;
33039             }
33040             
33041             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33042                 return;
33043             }
33044             
33045             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33046                 e.stopEvent();
33047             }
33048         };
33049         
33050         this.el.on("keypress", keyPress, this);
33051     },
33052     
33053     validateValue : function(value)
33054     {
33055         
33056         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33057             return false;
33058         }
33059         
33060         var num = this.parseValue(value);
33061         
33062         if(isNaN(num)){
33063             this.markInvalid(String.format(this.nanText, value));
33064             return false;
33065         }
33066         
33067         if(num < this.minValue){
33068             this.markInvalid(String.format(this.minText, this.minValue));
33069             return false;
33070         }
33071         
33072         if(num > this.maxValue){
33073             this.markInvalid(String.format(this.maxText, this.maxValue));
33074             return false;
33075         }
33076         
33077         return true;
33078     },
33079
33080     getValue : function()
33081     {
33082         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33083     },
33084
33085     parseValue : function(value)
33086     {
33087         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33088         return isNaN(value) ? '' : value;
33089     },
33090
33091     fixPrecision : function(value)
33092     {
33093         var nan = isNaN(value);
33094         
33095         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33096             return nan ? '' : value;
33097         }
33098         return parseFloat(value).toFixed(this.decimalPrecision);
33099     },
33100
33101     setValue : function(v)
33102     {
33103         v = this.fixPrecision(v);
33104         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33105     },
33106
33107     decimalPrecisionFcn : function(v)
33108     {
33109         return Math.floor(v);
33110     },
33111
33112     beforeBlur : function()
33113     {
33114         if(!this.castInt){
33115             return;
33116         }
33117         
33118         var v = this.parseValue(this.getRawValue());
33119         if(v){
33120             this.setValue(v);
33121         }
33122     }
33123     
33124 });
33125
33126  
33127
33128 /*
33129 * Licence: LGPL
33130 */
33131
33132 /**
33133  * @class Roo.bootstrap.DocumentSlider
33134  * @extends Roo.bootstrap.Component
33135  * Bootstrap DocumentSlider class
33136  * 
33137  * @constructor
33138  * Create a new DocumentViewer
33139  * @param {Object} config The config object
33140  */
33141
33142 Roo.bootstrap.DocumentSlider = function(config){
33143     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33144     
33145     this.files = [];
33146     
33147     this.addEvents({
33148         /**
33149          * @event initial
33150          * Fire after initEvent
33151          * @param {Roo.bootstrap.DocumentSlider} this
33152          */
33153         "initial" : true,
33154         /**
33155          * @event update
33156          * Fire after update
33157          * @param {Roo.bootstrap.DocumentSlider} this
33158          */
33159         "update" : true,
33160         /**
33161          * @event click
33162          * Fire after click
33163          * @param {Roo.bootstrap.DocumentSlider} this
33164          */
33165         "click" : true
33166     });
33167 };
33168
33169 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33170     
33171     files : false,
33172     
33173     indicator : 0,
33174     
33175     getAutoCreate : function()
33176     {
33177         var cfg = {
33178             tag : 'div',
33179             cls : 'roo-document-slider',
33180             cn : [
33181                 {
33182                     tag : 'div',
33183                     cls : 'roo-document-slider-header',
33184                     cn : [
33185                         {
33186                             tag : 'div',
33187                             cls : 'roo-document-slider-header-title'
33188                         }
33189                     ]
33190                 },
33191                 {
33192                     tag : 'div',
33193                     cls : 'roo-document-slider-body',
33194                     cn : [
33195                         {
33196                             tag : 'div',
33197                             cls : 'roo-document-slider-prev',
33198                             cn : [
33199                                 {
33200                                     tag : 'i',
33201                                     cls : 'fa fa-chevron-left'
33202                                 }
33203                             ]
33204                         },
33205                         {
33206                             tag : 'div',
33207                             cls : 'roo-document-slider-thumb',
33208                             cn : [
33209                                 {
33210                                     tag : 'img',
33211                                     cls : 'roo-document-slider-image'
33212                                 }
33213                             ]
33214                         },
33215                         {
33216                             tag : 'div',
33217                             cls : 'roo-document-slider-next',
33218                             cn : [
33219                                 {
33220                                     tag : 'i',
33221                                     cls : 'fa fa-chevron-right'
33222                                 }
33223                             ]
33224                         }
33225                     ]
33226                 }
33227             ]
33228         };
33229         
33230         return cfg;
33231     },
33232     
33233     initEvents : function()
33234     {
33235         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33236         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33237         
33238         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33239         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33240         
33241         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33242         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33243         
33244         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33245         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33246         
33247         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33248         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33249         
33250         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33251         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33252         
33253         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33254         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33255         
33256         this.thumbEl.on('click', this.onClick, this);
33257         
33258         this.prevIndicator.on('click', this.prev, this);
33259         
33260         this.nextIndicator.on('click', this.next, this);
33261         
33262     },
33263     
33264     initial : function()
33265     {
33266         if(this.files.length){
33267             this.indicator = 1;
33268             this.update()
33269         }
33270         
33271         this.fireEvent('initial', this);
33272     },
33273     
33274     update : function()
33275     {
33276         this.imageEl.attr('src', this.files[this.indicator - 1]);
33277         
33278         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33279         
33280         this.prevIndicator.show();
33281         
33282         if(this.indicator == 1){
33283             this.prevIndicator.hide();
33284         }
33285         
33286         this.nextIndicator.show();
33287         
33288         if(this.indicator == this.files.length){
33289             this.nextIndicator.hide();
33290         }
33291         
33292         this.thumbEl.scrollTo('top');
33293         
33294         this.fireEvent('update', this);
33295     },
33296     
33297     onClick : function(e)
33298     {
33299         e.preventDefault();
33300         
33301         this.fireEvent('click', this);
33302     },
33303     
33304     prev : function(e)
33305     {
33306         e.preventDefault();
33307         
33308         this.indicator = Math.max(1, this.indicator - 1);
33309         
33310         this.update();
33311     },
33312     
33313     next : function(e)
33314     {
33315         e.preventDefault();
33316         
33317         this.indicator = Math.min(this.files.length, this.indicator + 1);
33318         
33319         this.update();
33320     }
33321 });
33322 /*
33323  * - LGPL
33324  *
33325  * RadioSet
33326  *
33327  *
33328  */
33329
33330 /**
33331  * @class Roo.bootstrap.RadioSet
33332  * @extends Roo.bootstrap.Input
33333  * Bootstrap RadioSet class
33334  * @cfg {String} indicatorpos (left|right) default left
33335  * @cfg {Boolean} inline (true|false) inline the element (default true)
33336  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33337  * @constructor
33338  * Create a new RadioSet
33339  * @param {Object} config The config object
33340  */
33341
33342 Roo.bootstrap.RadioSet = function(config){
33343     
33344     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33345     
33346     this.radioes = [];
33347     
33348     Roo.bootstrap.RadioSet.register(this);
33349     
33350     this.addEvents({
33351         /**
33352         * @event check
33353         * Fires when the element is checked or unchecked.
33354         * @param {Roo.bootstrap.RadioSet} this This radio
33355         * @param {Roo.bootstrap.Radio} item The checked item
33356         */
33357        check : true
33358     });
33359     
33360 };
33361
33362 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33363
33364     radioes : false,
33365     
33366     inline : true,
33367     
33368     weight : '',
33369     
33370     indicatorpos : 'left',
33371     
33372     getAutoCreate : function()
33373     {
33374         var label = {
33375             tag : 'label',
33376             cls : 'roo-radio-set-label',
33377             cn : [
33378                 {
33379                     tag : 'span',
33380                     html : this.fieldLabel
33381                 }
33382             ]
33383         };
33384         
33385         if(this.indicatorpos == 'left'){
33386             label.cn.unshift({
33387                 tag : 'i',
33388                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33389                 tooltip : 'This field is required'
33390             });
33391         } else {
33392             label.cn.push({
33393                 tag : 'i',
33394                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33395                 tooltip : 'This field is required'
33396             });
33397         }
33398         
33399         var items = {
33400             tag : 'div',
33401             cls : 'roo-radio-set-items'
33402         };
33403         
33404         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33405         
33406         if (align === 'left' && this.fieldLabel.length) {
33407             
33408             items = {
33409                 cls : "roo-radio-set-right", 
33410                 cn: [
33411                     items
33412                 ]
33413             };
33414             
33415             if(this.labelWidth > 12){
33416                 label.style = "width: " + this.labelWidth + 'px';
33417             }
33418             
33419             if(this.labelWidth < 13 && this.labelmd == 0){
33420                 this.labelmd = this.labelWidth;
33421             }
33422             
33423             if(this.labellg > 0){
33424                 label.cls += ' col-lg-' + this.labellg;
33425                 items.cls += ' col-lg-' + (12 - this.labellg);
33426             }
33427             
33428             if(this.labelmd > 0){
33429                 label.cls += ' col-md-' + this.labelmd;
33430                 items.cls += ' col-md-' + (12 - this.labelmd);
33431             }
33432             
33433             if(this.labelsm > 0){
33434                 label.cls += ' col-sm-' + this.labelsm;
33435                 items.cls += ' col-sm-' + (12 - this.labelsm);
33436             }
33437             
33438             if(this.labelxs > 0){
33439                 label.cls += ' col-xs-' + this.labelxs;
33440                 items.cls += ' col-xs-' + (12 - this.labelxs);
33441             }
33442         }
33443         
33444         var cfg = {
33445             tag : 'div',
33446             cls : 'roo-radio-set',
33447             cn : [
33448                 {
33449                     tag : 'input',
33450                     cls : 'roo-radio-set-input',
33451                     type : 'hidden',
33452                     name : this.name,
33453                     value : this.value ? this.value :  ''
33454                 },
33455                 label,
33456                 items
33457             ]
33458         };
33459         
33460         if(this.weight.length){
33461             cfg.cls += ' roo-radio-' + this.weight;
33462         }
33463         
33464         if(this.inline) {
33465             cfg.cls += ' roo-radio-set-inline';
33466         }
33467         
33468         var settings=this;
33469         ['xs','sm','md','lg'].map(function(size){
33470             if (settings[size]) {
33471                 cfg.cls += ' col-' + size + '-' + settings[size];
33472             }
33473         });
33474         
33475         return cfg;
33476         
33477     },
33478
33479     initEvents : function()
33480     {
33481         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33482         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33483         
33484         if(!this.fieldLabel.length){
33485             this.labelEl.hide();
33486         }
33487         
33488         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33489         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33490         
33491         this.indicatorEl().addClass('invisible');
33492         
33493         this.originalValue = this.getValue();
33494         
33495     },
33496     
33497     inputEl: function ()
33498     {
33499         return this.el.select('.roo-radio-set-input', true).first();
33500     },
33501     
33502     getChildContainer : function()
33503     {
33504         return this.itemsEl;
33505     },
33506     
33507     register : function(item)
33508     {
33509         this.radioes.push(item);
33510         
33511     },
33512     
33513     validate : function()
33514     {   
33515         var valid = false;
33516         
33517         Roo.each(this.radioes, function(i){
33518             if(!i.checked){
33519                 return;
33520             }
33521             
33522             valid = true;
33523             return false;
33524         });
33525         
33526         if(this.allowBlank) {
33527             return true;
33528         }
33529         
33530         if(this.disabled || valid){
33531             this.markValid();
33532             return true;
33533         }
33534         
33535         this.markInvalid();
33536         return false;
33537         
33538     },
33539     
33540     markValid : function()
33541     {
33542         if(this.labelEl.isVisible(true)){
33543             this.indicatorEl().removeClass('visible');
33544             this.indicatorEl().addClass('invisible');
33545         }
33546         
33547         this.el.removeClass([this.invalidClass, this.validClass]);
33548         this.el.addClass(this.validClass);
33549         
33550         this.fireEvent('valid', this);
33551     },
33552     
33553     markInvalid : function(msg)
33554     {
33555         if(this.allowBlank || this.disabled){
33556             return;
33557         }
33558         
33559         if(this.labelEl.isVisible(true)){
33560             this.indicatorEl().removeClass('invisible');
33561             this.indicatorEl().addClass('visible');
33562         }
33563         
33564         this.el.removeClass([this.invalidClass, this.validClass]);
33565         this.el.addClass(this.invalidClass);
33566         
33567         this.fireEvent('invalid', this, msg);
33568         
33569     },
33570     
33571     setValue : function(v, suppressEvent)
33572     {   
33573         if(this.value === v){
33574             return;
33575         }
33576         
33577         this.value = v;
33578         
33579         if(this.rendered){
33580             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33581         }
33582         
33583         Roo.each(this.radioes, function(i){
33584             
33585             i.checked = false;
33586             i.el.removeClass('checked');
33587             
33588             if(i.value === v || i.value.toString() === v.toString()){
33589                 i.checked = true;
33590                 i.el.addClass('checked');
33591                 
33592                 if(suppressEvent !== true){
33593                     this.fireEvent('check', this, i);
33594                 }
33595             }
33596             
33597         }, this);
33598         
33599         this.validate();
33600     },
33601     
33602     clearInvalid : function(){
33603         
33604         if(!this.el || this.preventMark){
33605             return;
33606         }
33607         
33608         this.el.removeClass([this.invalidClass]);
33609         
33610         this.fireEvent('valid', this);
33611     }
33612     
33613 });
33614
33615 Roo.apply(Roo.bootstrap.RadioSet, {
33616     
33617     groups: {},
33618     
33619     register : function(set)
33620     {
33621         this.groups[set.name] = set;
33622     },
33623     
33624     get: function(name) 
33625     {
33626         if (typeof(this.groups[name]) == 'undefined') {
33627             return false;
33628         }
33629         
33630         return this.groups[name] ;
33631     }
33632     
33633 });
33634 /*
33635  * Based on:
33636  * Ext JS Library 1.1.1
33637  * Copyright(c) 2006-2007, Ext JS, LLC.
33638  *
33639  * Originally Released Under LGPL - original licence link has changed is not relivant.
33640  *
33641  * Fork - LGPL
33642  * <script type="text/javascript">
33643  */
33644
33645
33646 /**
33647  * @class Roo.bootstrap.SplitBar
33648  * @extends Roo.util.Observable
33649  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33650  * <br><br>
33651  * Usage:
33652  * <pre><code>
33653 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33654                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33655 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33656 split.minSize = 100;
33657 split.maxSize = 600;
33658 split.animate = true;
33659 split.on('moved', splitterMoved);
33660 </code></pre>
33661  * @constructor
33662  * Create a new SplitBar
33663  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33664  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33665  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33666  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33667                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33668                         position of the SplitBar).
33669  */
33670 Roo.bootstrap.SplitBar = function(cfg){
33671     
33672     /** @private */
33673     
33674     //{
33675     //  dragElement : elm
33676     //  resizingElement: el,
33677         // optional..
33678     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33679     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33680         // existingProxy ???
33681     //}
33682     
33683     this.el = Roo.get(cfg.dragElement, true);
33684     this.el.dom.unselectable = "on";
33685     /** @private */
33686     this.resizingEl = Roo.get(cfg.resizingElement, true);
33687
33688     /**
33689      * @private
33690      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33691      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33692      * @type Number
33693      */
33694     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33695     
33696     /**
33697      * The minimum size of the resizing element. (Defaults to 0)
33698      * @type Number
33699      */
33700     this.minSize = 0;
33701     
33702     /**
33703      * The maximum size of the resizing element. (Defaults to 2000)
33704      * @type Number
33705      */
33706     this.maxSize = 2000;
33707     
33708     /**
33709      * Whether to animate the transition to the new size
33710      * @type Boolean
33711      */
33712     this.animate = false;
33713     
33714     /**
33715      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33716      * @type Boolean
33717      */
33718     this.useShim = false;
33719     
33720     /** @private */
33721     this.shim = null;
33722     
33723     if(!cfg.existingProxy){
33724         /** @private */
33725         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33726     }else{
33727         this.proxy = Roo.get(cfg.existingProxy).dom;
33728     }
33729     /** @private */
33730     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33731     
33732     /** @private */
33733     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33734     
33735     /** @private */
33736     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33737     
33738     /** @private */
33739     this.dragSpecs = {};
33740     
33741     /**
33742      * @private The adapter to use to positon and resize elements
33743      */
33744     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33745     this.adapter.init(this);
33746     
33747     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33748         /** @private */
33749         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33750         this.el.addClass("roo-splitbar-h");
33751     }else{
33752         /** @private */
33753         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33754         this.el.addClass("roo-splitbar-v");
33755     }
33756     
33757     this.addEvents({
33758         /**
33759          * @event resize
33760          * Fires when the splitter is moved (alias for {@link #event-moved})
33761          * @param {Roo.bootstrap.SplitBar} this
33762          * @param {Number} newSize the new width or height
33763          */
33764         "resize" : true,
33765         /**
33766          * @event moved
33767          * Fires when the splitter is moved
33768          * @param {Roo.bootstrap.SplitBar} this
33769          * @param {Number} newSize the new width or height
33770          */
33771         "moved" : true,
33772         /**
33773          * @event beforeresize
33774          * Fires before the splitter is dragged
33775          * @param {Roo.bootstrap.SplitBar} this
33776          */
33777         "beforeresize" : true,
33778
33779         "beforeapply" : true
33780     });
33781
33782     Roo.util.Observable.call(this);
33783 };
33784
33785 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33786     onStartProxyDrag : function(x, y){
33787         this.fireEvent("beforeresize", this);
33788         if(!this.overlay){
33789             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33790             o.unselectable();
33791             o.enableDisplayMode("block");
33792             // all splitbars share the same overlay
33793             Roo.bootstrap.SplitBar.prototype.overlay = o;
33794         }
33795         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33796         this.overlay.show();
33797         Roo.get(this.proxy).setDisplayed("block");
33798         var size = this.adapter.getElementSize(this);
33799         this.activeMinSize = this.getMinimumSize();;
33800         this.activeMaxSize = this.getMaximumSize();;
33801         var c1 = size - this.activeMinSize;
33802         var c2 = Math.max(this.activeMaxSize - size, 0);
33803         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33804             this.dd.resetConstraints();
33805             this.dd.setXConstraint(
33806                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33807                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33808             );
33809             this.dd.setYConstraint(0, 0);
33810         }else{
33811             this.dd.resetConstraints();
33812             this.dd.setXConstraint(0, 0);
33813             this.dd.setYConstraint(
33814                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33815                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33816             );
33817          }
33818         this.dragSpecs.startSize = size;
33819         this.dragSpecs.startPoint = [x, y];
33820         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33821     },
33822     
33823     /** 
33824      * @private Called after the drag operation by the DDProxy
33825      */
33826     onEndProxyDrag : function(e){
33827         Roo.get(this.proxy).setDisplayed(false);
33828         var endPoint = Roo.lib.Event.getXY(e);
33829         if(this.overlay){
33830             this.overlay.hide();
33831         }
33832         var newSize;
33833         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33834             newSize = this.dragSpecs.startSize + 
33835                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33836                     endPoint[0] - this.dragSpecs.startPoint[0] :
33837                     this.dragSpecs.startPoint[0] - endPoint[0]
33838                 );
33839         }else{
33840             newSize = this.dragSpecs.startSize + 
33841                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33842                     endPoint[1] - this.dragSpecs.startPoint[1] :
33843                     this.dragSpecs.startPoint[1] - endPoint[1]
33844                 );
33845         }
33846         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33847         if(newSize != this.dragSpecs.startSize){
33848             if(this.fireEvent('beforeapply', this, newSize) !== false){
33849                 this.adapter.setElementSize(this, newSize);
33850                 this.fireEvent("moved", this, newSize);
33851                 this.fireEvent("resize", this, newSize);
33852             }
33853         }
33854     },
33855     
33856     /**
33857      * Get the adapter this SplitBar uses
33858      * @return The adapter object
33859      */
33860     getAdapter : function(){
33861         return this.adapter;
33862     },
33863     
33864     /**
33865      * Set the adapter this SplitBar uses
33866      * @param {Object} adapter A SplitBar adapter object
33867      */
33868     setAdapter : function(adapter){
33869         this.adapter = adapter;
33870         this.adapter.init(this);
33871     },
33872     
33873     /**
33874      * Gets the minimum size for the resizing element
33875      * @return {Number} The minimum size
33876      */
33877     getMinimumSize : function(){
33878         return this.minSize;
33879     },
33880     
33881     /**
33882      * Sets the minimum size for the resizing element
33883      * @param {Number} minSize The minimum size
33884      */
33885     setMinimumSize : function(minSize){
33886         this.minSize = minSize;
33887     },
33888     
33889     /**
33890      * Gets the maximum size for the resizing element
33891      * @return {Number} The maximum size
33892      */
33893     getMaximumSize : function(){
33894         return this.maxSize;
33895     },
33896     
33897     /**
33898      * Sets the maximum size for the resizing element
33899      * @param {Number} maxSize The maximum size
33900      */
33901     setMaximumSize : function(maxSize){
33902         this.maxSize = maxSize;
33903     },
33904     
33905     /**
33906      * Sets the initialize size for the resizing element
33907      * @param {Number} size The initial size
33908      */
33909     setCurrentSize : function(size){
33910         var oldAnimate = this.animate;
33911         this.animate = false;
33912         this.adapter.setElementSize(this, size);
33913         this.animate = oldAnimate;
33914     },
33915     
33916     /**
33917      * Destroy this splitbar. 
33918      * @param {Boolean} removeEl True to remove the element
33919      */
33920     destroy : function(removeEl){
33921         if(this.shim){
33922             this.shim.remove();
33923         }
33924         this.dd.unreg();
33925         this.proxy.parentNode.removeChild(this.proxy);
33926         if(removeEl){
33927             this.el.remove();
33928         }
33929     }
33930 });
33931
33932 /**
33933  * @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.
33934  */
33935 Roo.bootstrap.SplitBar.createProxy = function(dir){
33936     var proxy = new Roo.Element(document.createElement("div"));
33937     proxy.unselectable();
33938     var cls = 'roo-splitbar-proxy';
33939     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33940     document.body.appendChild(proxy.dom);
33941     return proxy.dom;
33942 };
33943
33944 /** 
33945  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33946  * Default Adapter. It assumes the splitter and resizing element are not positioned
33947  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33948  */
33949 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33950 };
33951
33952 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33953     // do nothing for now
33954     init : function(s){
33955     
33956     },
33957     /**
33958      * Called before drag operations to get the current size of the resizing element. 
33959      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33960      */
33961      getElementSize : function(s){
33962         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33963             return s.resizingEl.getWidth();
33964         }else{
33965             return s.resizingEl.getHeight();
33966         }
33967     },
33968     
33969     /**
33970      * Called after drag operations to set the size of the resizing element.
33971      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33972      * @param {Number} newSize The new size to set
33973      * @param {Function} onComplete A function to be invoked when resizing is complete
33974      */
33975     setElementSize : function(s, newSize, onComplete){
33976         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33977             if(!s.animate){
33978                 s.resizingEl.setWidth(newSize);
33979                 if(onComplete){
33980                     onComplete(s, newSize);
33981                 }
33982             }else{
33983                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33984             }
33985         }else{
33986             
33987             if(!s.animate){
33988                 s.resizingEl.setHeight(newSize);
33989                 if(onComplete){
33990                     onComplete(s, newSize);
33991                 }
33992             }else{
33993                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33994             }
33995         }
33996     }
33997 };
33998
33999 /** 
34000  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34001  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34002  * Adapter that  moves the splitter element to align with the resized sizing element. 
34003  * Used with an absolute positioned SplitBar.
34004  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34005  * document.body, make sure you assign an id to the body element.
34006  */
34007 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34008     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34009     this.container = Roo.get(container);
34010 };
34011
34012 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34013     init : function(s){
34014         this.basic.init(s);
34015     },
34016     
34017     getElementSize : function(s){
34018         return this.basic.getElementSize(s);
34019     },
34020     
34021     setElementSize : function(s, newSize, onComplete){
34022         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34023     },
34024     
34025     moveSplitter : function(s){
34026         var yes = Roo.bootstrap.SplitBar;
34027         switch(s.placement){
34028             case yes.LEFT:
34029                 s.el.setX(s.resizingEl.getRight());
34030                 break;
34031             case yes.RIGHT:
34032                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34033                 break;
34034             case yes.TOP:
34035                 s.el.setY(s.resizingEl.getBottom());
34036                 break;
34037             case yes.BOTTOM:
34038                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34039                 break;
34040         }
34041     }
34042 };
34043
34044 /**
34045  * Orientation constant - Create a vertical SplitBar
34046  * @static
34047  * @type Number
34048  */
34049 Roo.bootstrap.SplitBar.VERTICAL = 1;
34050
34051 /**
34052  * Orientation constant - Create a horizontal SplitBar
34053  * @static
34054  * @type Number
34055  */
34056 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34057
34058 /**
34059  * Placement constant - The resizing element is to the left of the splitter element
34060  * @static
34061  * @type Number
34062  */
34063 Roo.bootstrap.SplitBar.LEFT = 1;
34064
34065 /**
34066  * Placement constant - The resizing element is to the right of the splitter element
34067  * @static
34068  * @type Number
34069  */
34070 Roo.bootstrap.SplitBar.RIGHT = 2;
34071
34072 /**
34073  * Placement constant - The resizing element is positioned above the splitter element
34074  * @static
34075  * @type Number
34076  */
34077 Roo.bootstrap.SplitBar.TOP = 3;
34078
34079 /**
34080  * Placement constant - The resizing element is positioned under splitter element
34081  * @static
34082  * @type Number
34083  */
34084 Roo.bootstrap.SplitBar.BOTTOM = 4;
34085 Roo.namespace("Roo.bootstrap.layout");/*
34086  * Based on:
34087  * Ext JS Library 1.1.1
34088  * Copyright(c) 2006-2007, Ext JS, LLC.
34089  *
34090  * Originally Released Under LGPL - original licence link has changed is not relivant.
34091  *
34092  * Fork - LGPL
34093  * <script type="text/javascript">
34094  */
34095
34096 /**
34097  * @class Roo.bootstrap.layout.Manager
34098  * @extends Roo.bootstrap.Component
34099  * Base class for layout managers.
34100  */
34101 Roo.bootstrap.layout.Manager = function(config)
34102 {
34103     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34104
34105
34106
34107
34108
34109     /** false to disable window resize monitoring @type Boolean */
34110     this.monitorWindowResize = true;
34111     this.regions = {};
34112     this.addEvents({
34113         /**
34114          * @event layout
34115          * Fires when a layout is performed.
34116          * @param {Roo.LayoutManager} this
34117          */
34118         "layout" : true,
34119         /**
34120          * @event regionresized
34121          * Fires when the user resizes a region.
34122          * @param {Roo.LayoutRegion} region The resized region
34123          * @param {Number} newSize The new size (width for east/west, height for north/south)
34124          */
34125         "regionresized" : true,
34126         /**
34127          * @event regioncollapsed
34128          * Fires when a region is collapsed.
34129          * @param {Roo.LayoutRegion} region The collapsed region
34130          */
34131         "regioncollapsed" : true,
34132         /**
34133          * @event regionexpanded
34134          * Fires when a region is expanded.
34135          * @param {Roo.LayoutRegion} region The expanded region
34136          */
34137         "regionexpanded" : true
34138     });
34139     this.updating = false;
34140
34141     if (config.el) {
34142         this.el = Roo.get(config.el);
34143         this.initEvents();
34144     }
34145
34146 };
34147
34148 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34149
34150
34151     regions : null,
34152
34153     monitorWindowResize : true,
34154
34155
34156     updating : false,
34157
34158
34159     onRender : function(ct, position)
34160     {
34161         if(!this.el){
34162             this.el = Roo.get(ct);
34163             this.initEvents();
34164         }
34165         //this.fireEvent('render',this);
34166     },
34167
34168
34169     initEvents: function()
34170     {
34171
34172
34173         // ie scrollbar fix
34174         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34175             document.body.scroll = "no";
34176         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34177             this.el.position('relative');
34178         }
34179         this.id = this.el.id;
34180         this.el.addClass("roo-layout-container");
34181         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34182         if(this.el.dom != document.body ) {
34183             this.el.on('resize', this.layout,this);
34184             this.el.on('show', this.layout,this);
34185         }
34186
34187     },
34188
34189     /**
34190      * Returns true if this layout is currently being updated
34191      * @return {Boolean}
34192      */
34193     isUpdating : function(){
34194         return this.updating;
34195     },
34196
34197     /**
34198      * Suspend the LayoutManager from doing auto-layouts while
34199      * making multiple add or remove calls
34200      */
34201     beginUpdate : function(){
34202         this.updating = true;
34203     },
34204
34205     /**
34206      * Restore auto-layouts and optionally disable the manager from performing a layout
34207      * @param {Boolean} noLayout true to disable a layout update
34208      */
34209     endUpdate : function(noLayout){
34210         this.updating = false;
34211         if(!noLayout){
34212             this.layout();
34213         }
34214     },
34215
34216     layout: function(){
34217         // abstract...
34218     },
34219
34220     onRegionResized : function(region, newSize){
34221         this.fireEvent("regionresized", region, newSize);
34222         this.layout();
34223     },
34224
34225     onRegionCollapsed : function(region){
34226         this.fireEvent("regioncollapsed", region);
34227     },
34228
34229     onRegionExpanded : function(region){
34230         this.fireEvent("regionexpanded", region);
34231     },
34232
34233     /**
34234      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34235      * performs box-model adjustments.
34236      * @return {Object} The size as an object {width: (the width), height: (the height)}
34237      */
34238     getViewSize : function()
34239     {
34240         var size;
34241         if(this.el.dom != document.body){
34242             size = this.el.getSize();
34243         }else{
34244             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34245         }
34246         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34247         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34248         return size;
34249     },
34250
34251     /**
34252      * Returns the Element this layout is bound to.
34253      * @return {Roo.Element}
34254      */
34255     getEl : function(){
34256         return this.el;
34257     },
34258
34259     /**
34260      * Returns the specified region.
34261      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34262      * @return {Roo.LayoutRegion}
34263      */
34264     getRegion : function(target){
34265         return this.regions[target.toLowerCase()];
34266     },
34267
34268     onWindowResize : function(){
34269         if(this.monitorWindowResize){
34270             this.layout();
34271         }
34272     }
34273 });
34274 /*
34275  * Based on:
34276  * Ext JS Library 1.1.1
34277  * Copyright(c) 2006-2007, Ext JS, LLC.
34278  *
34279  * Originally Released Under LGPL - original licence link has changed is not relivant.
34280  *
34281  * Fork - LGPL
34282  * <script type="text/javascript">
34283  */
34284 /**
34285  * @class Roo.bootstrap.layout.Border
34286  * @extends Roo.bootstrap.layout.Manager
34287  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34288  * please see: examples/bootstrap/nested.html<br><br>
34289  
34290 <b>The container the layout is rendered into can be either the body element or any other element.
34291 If it is not the body element, the container needs to either be an absolute positioned element,
34292 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34293 the container size if it is not the body element.</b>
34294
34295 * @constructor
34296 * Create a new Border
34297 * @param {Object} config Configuration options
34298  */
34299 Roo.bootstrap.layout.Border = function(config){
34300     config = config || {};
34301     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34302     
34303     
34304     
34305     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34306         if(config[region]){
34307             config[region].region = region;
34308             this.addRegion(config[region]);
34309         }
34310     },this);
34311     
34312 };
34313
34314 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34315
34316 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34317     /**
34318      * Creates and adds a new region if it doesn't already exist.
34319      * @param {String} target The target region key (north, south, east, west or center).
34320      * @param {Object} config The regions config object
34321      * @return {BorderLayoutRegion} The new region
34322      */
34323     addRegion : function(config)
34324     {
34325         if(!this.regions[config.region]){
34326             var r = this.factory(config);
34327             this.bindRegion(r);
34328         }
34329         return this.regions[config.region];
34330     },
34331
34332     // private (kinda)
34333     bindRegion : function(r){
34334         this.regions[r.config.region] = r;
34335         
34336         r.on("visibilitychange",    this.layout, this);
34337         r.on("paneladded",          this.layout, this);
34338         r.on("panelremoved",        this.layout, this);
34339         r.on("invalidated",         this.layout, this);
34340         r.on("resized",             this.onRegionResized, this);
34341         r.on("collapsed",           this.onRegionCollapsed, this);
34342         r.on("expanded",            this.onRegionExpanded, this);
34343     },
34344
34345     /**
34346      * Performs a layout update.
34347      */
34348     layout : function()
34349     {
34350         if(this.updating) {
34351             return;
34352         }
34353         
34354         // render all the rebions if they have not been done alreayd?
34355         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34356             if(this.regions[region] && !this.regions[region].bodyEl){
34357                 this.regions[region].onRender(this.el)
34358             }
34359         },this);
34360         
34361         var size = this.getViewSize();
34362         var w = size.width;
34363         var h = size.height;
34364         var centerW = w;
34365         var centerH = h;
34366         var centerY = 0;
34367         var centerX = 0;
34368         //var x = 0, y = 0;
34369
34370         var rs = this.regions;
34371         var north = rs["north"];
34372         var south = rs["south"]; 
34373         var west = rs["west"];
34374         var east = rs["east"];
34375         var center = rs["center"];
34376         //if(this.hideOnLayout){ // not supported anymore
34377             //c.el.setStyle("display", "none");
34378         //}
34379         if(north && north.isVisible()){
34380             var b = north.getBox();
34381             var m = north.getMargins();
34382             b.width = w - (m.left+m.right);
34383             b.x = m.left;
34384             b.y = m.top;
34385             centerY = b.height + b.y + m.bottom;
34386             centerH -= centerY;
34387             north.updateBox(this.safeBox(b));
34388         }
34389         if(south && south.isVisible()){
34390             var b = south.getBox();
34391             var m = south.getMargins();
34392             b.width = w - (m.left+m.right);
34393             b.x = m.left;
34394             var totalHeight = (b.height + m.top + m.bottom);
34395             b.y = h - totalHeight + m.top;
34396             centerH -= totalHeight;
34397             south.updateBox(this.safeBox(b));
34398         }
34399         if(west && west.isVisible()){
34400             var b = west.getBox();
34401             var m = west.getMargins();
34402             b.height = centerH - (m.top+m.bottom);
34403             b.x = m.left;
34404             b.y = centerY + m.top;
34405             var totalWidth = (b.width + m.left + m.right);
34406             centerX += totalWidth;
34407             centerW -= totalWidth;
34408             west.updateBox(this.safeBox(b));
34409         }
34410         if(east && east.isVisible()){
34411             var b = east.getBox();
34412             var m = east.getMargins();
34413             b.height = centerH - (m.top+m.bottom);
34414             var totalWidth = (b.width + m.left + m.right);
34415             b.x = w - totalWidth + m.left;
34416             b.y = centerY + m.top;
34417             centerW -= totalWidth;
34418             east.updateBox(this.safeBox(b));
34419         }
34420         if(center){
34421             var m = center.getMargins();
34422             var centerBox = {
34423                 x: centerX + m.left,
34424                 y: centerY + m.top,
34425                 width: centerW - (m.left+m.right),
34426                 height: centerH - (m.top+m.bottom)
34427             };
34428             //if(this.hideOnLayout){
34429                 //center.el.setStyle("display", "block");
34430             //}
34431             center.updateBox(this.safeBox(centerBox));
34432         }
34433         this.el.repaint();
34434         this.fireEvent("layout", this);
34435     },
34436
34437     // private
34438     safeBox : function(box){
34439         box.width = Math.max(0, box.width);
34440         box.height = Math.max(0, box.height);
34441         return box;
34442     },
34443
34444     /**
34445      * Adds a ContentPanel (or subclass) to this layout.
34446      * @param {String} target The target region key (north, south, east, west or center).
34447      * @param {Roo.ContentPanel} panel The panel to add
34448      * @return {Roo.ContentPanel} The added panel
34449      */
34450     add : function(target, panel){
34451          
34452         target = target.toLowerCase();
34453         return this.regions[target].add(panel);
34454     },
34455
34456     /**
34457      * Remove a ContentPanel (or subclass) to this layout.
34458      * @param {String} target The target region key (north, south, east, west or center).
34459      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34460      * @return {Roo.ContentPanel} The removed panel
34461      */
34462     remove : function(target, panel){
34463         target = target.toLowerCase();
34464         return this.regions[target].remove(panel);
34465     },
34466
34467     /**
34468      * Searches all regions for a panel with the specified id
34469      * @param {String} panelId
34470      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34471      */
34472     findPanel : function(panelId){
34473         var rs = this.regions;
34474         for(var target in rs){
34475             if(typeof rs[target] != "function"){
34476                 var p = rs[target].getPanel(panelId);
34477                 if(p){
34478                     return p;
34479                 }
34480             }
34481         }
34482         return null;
34483     },
34484
34485     /**
34486      * Searches all regions for a panel with the specified id and activates (shows) it.
34487      * @param {String/ContentPanel} panelId The panels id or the panel itself
34488      * @return {Roo.ContentPanel} The shown panel or null
34489      */
34490     showPanel : function(panelId) {
34491       var rs = this.regions;
34492       for(var target in rs){
34493          var r = rs[target];
34494          if(typeof r != "function"){
34495             if(r.hasPanel(panelId)){
34496                return r.showPanel(panelId);
34497             }
34498          }
34499       }
34500       return null;
34501    },
34502
34503    /**
34504      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34505      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34506      */
34507    /*
34508     restoreState : function(provider){
34509         if(!provider){
34510             provider = Roo.state.Manager;
34511         }
34512         var sm = new Roo.LayoutStateManager();
34513         sm.init(this, provider);
34514     },
34515 */
34516  
34517  
34518     /**
34519      * Adds a xtype elements to the layout.
34520      * <pre><code>
34521
34522 layout.addxtype({
34523        xtype : 'ContentPanel',
34524        region: 'west',
34525        items: [ .... ]
34526    }
34527 );
34528
34529 layout.addxtype({
34530         xtype : 'NestedLayoutPanel',
34531         region: 'west',
34532         layout: {
34533            center: { },
34534            west: { }   
34535         },
34536         items : [ ... list of content panels or nested layout panels.. ]
34537    }
34538 );
34539 </code></pre>
34540      * @param {Object} cfg Xtype definition of item to add.
34541      */
34542     addxtype : function(cfg)
34543     {
34544         // basically accepts a pannel...
34545         // can accept a layout region..!?!?
34546         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34547         
34548         
34549         // theory?  children can only be panels??
34550         
34551         //if (!cfg.xtype.match(/Panel$/)) {
34552         //    return false;
34553         //}
34554         var ret = false;
34555         
34556         if (typeof(cfg.region) == 'undefined') {
34557             Roo.log("Failed to add Panel, region was not set");
34558             Roo.log(cfg);
34559             return false;
34560         }
34561         var region = cfg.region;
34562         delete cfg.region;
34563         
34564           
34565         var xitems = [];
34566         if (cfg.items) {
34567             xitems = cfg.items;
34568             delete cfg.items;
34569         }
34570         var nb = false;
34571         
34572         switch(cfg.xtype) 
34573         {
34574             case 'Content':  // ContentPanel (el, cfg)
34575             case 'Scroll':  // ContentPanel (el, cfg)
34576             case 'View': 
34577                 cfg.autoCreate = true;
34578                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34579                 //} else {
34580                 //    var el = this.el.createChild();
34581                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34582                 //}
34583                 
34584                 this.add(region, ret);
34585                 break;
34586             
34587             /*
34588             case 'TreePanel': // our new panel!
34589                 cfg.el = this.el.createChild();
34590                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34591                 this.add(region, ret);
34592                 break;
34593             */
34594             
34595             case 'Nest': 
34596                 // create a new Layout (which is  a Border Layout...
34597                 
34598                 var clayout = cfg.layout;
34599                 clayout.el  = this.el.createChild();
34600                 clayout.items   = clayout.items  || [];
34601                 
34602                 delete cfg.layout;
34603                 
34604                 // replace this exitems with the clayout ones..
34605                 xitems = clayout.items;
34606                  
34607                 // force background off if it's in center...
34608                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34609                     cfg.background = false;
34610                 }
34611                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34612                 
34613                 
34614                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34615                 //console.log('adding nested layout panel '  + cfg.toSource());
34616                 this.add(region, ret);
34617                 nb = {}; /// find first...
34618                 break;
34619             
34620             case 'Grid':
34621                 
34622                 // needs grid and region
34623                 
34624                 //var el = this.getRegion(region).el.createChild();
34625                 /*
34626                  *var el = this.el.createChild();
34627                 // create the grid first...
34628                 cfg.grid.container = el;
34629                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34630                 */
34631                 
34632                 if (region == 'center' && this.active ) {
34633                     cfg.background = false;
34634                 }
34635                 
34636                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34637                 
34638                 this.add(region, ret);
34639                 /*
34640                 if (cfg.background) {
34641                     // render grid on panel activation (if panel background)
34642                     ret.on('activate', function(gp) {
34643                         if (!gp.grid.rendered) {
34644                     //        gp.grid.render(el);
34645                         }
34646                     });
34647                 } else {
34648                   //  cfg.grid.render(el);
34649                 }
34650                 */
34651                 break;
34652            
34653            
34654             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34655                 // it was the old xcomponent building that caused this before.
34656                 // espeically if border is the top element in the tree.
34657                 ret = this;
34658                 break; 
34659                 
34660                     
34661                 
34662                 
34663                 
34664             default:
34665                 /*
34666                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34667                     
34668                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34669                     this.add(region, ret);
34670                 } else {
34671                 */
34672                     Roo.log(cfg);
34673                     throw "Can not add '" + cfg.xtype + "' to Border";
34674                     return null;
34675              
34676                                 
34677              
34678         }
34679         this.beginUpdate();
34680         // add children..
34681         var region = '';
34682         var abn = {};
34683         Roo.each(xitems, function(i)  {
34684             region = nb && i.region ? i.region : false;
34685             
34686             var add = ret.addxtype(i);
34687            
34688             if (region) {
34689                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34690                 if (!i.background) {
34691                     abn[region] = nb[region] ;
34692                 }
34693             }
34694             
34695         });
34696         this.endUpdate();
34697
34698         // make the last non-background panel active..
34699         //if (nb) { Roo.log(abn); }
34700         if (nb) {
34701             
34702             for(var r in abn) {
34703                 region = this.getRegion(r);
34704                 if (region) {
34705                     // tried using nb[r], but it does not work..
34706                      
34707                     region.showPanel(abn[r]);
34708                    
34709                 }
34710             }
34711         }
34712         return ret;
34713         
34714     },
34715     
34716     
34717 // private
34718     factory : function(cfg)
34719     {
34720         
34721         var validRegions = Roo.bootstrap.layout.Border.regions;
34722
34723         var target = cfg.region;
34724         cfg.mgr = this;
34725         
34726         var r = Roo.bootstrap.layout;
34727         Roo.log(target);
34728         switch(target){
34729             case "north":
34730                 return new r.North(cfg);
34731             case "south":
34732                 return new r.South(cfg);
34733             case "east":
34734                 return new r.East(cfg);
34735             case "west":
34736                 return new r.West(cfg);
34737             case "center":
34738                 return new r.Center(cfg);
34739         }
34740         throw 'Layout region "'+target+'" not supported.';
34741     }
34742     
34743     
34744 });
34745  /*
34746  * Based on:
34747  * Ext JS Library 1.1.1
34748  * Copyright(c) 2006-2007, Ext JS, LLC.
34749  *
34750  * Originally Released Under LGPL - original licence link has changed is not relivant.
34751  *
34752  * Fork - LGPL
34753  * <script type="text/javascript">
34754  */
34755  
34756 /**
34757  * @class Roo.bootstrap.layout.Basic
34758  * @extends Roo.util.Observable
34759  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34760  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34761  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34762  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34763  * @cfg {string}   region  the region that it inhabits..
34764  * @cfg {bool}   skipConfig skip config?
34765  * 
34766
34767  */
34768 Roo.bootstrap.layout.Basic = function(config){
34769     
34770     this.mgr = config.mgr;
34771     
34772     this.position = config.region;
34773     
34774     var skipConfig = config.skipConfig;
34775     
34776     this.events = {
34777         /**
34778          * @scope Roo.BasicLayoutRegion
34779          */
34780         
34781         /**
34782          * @event beforeremove
34783          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34784          * @param {Roo.LayoutRegion} this
34785          * @param {Roo.ContentPanel} panel The panel
34786          * @param {Object} e The cancel event object
34787          */
34788         "beforeremove" : true,
34789         /**
34790          * @event invalidated
34791          * Fires when the layout for this region is changed.
34792          * @param {Roo.LayoutRegion} this
34793          */
34794         "invalidated" : true,
34795         /**
34796          * @event visibilitychange
34797          * Fires when this region is shown or hidden 
34798          * @param {Roo.LayoutRegion} this
34799          * @param {Boolean} visibility true or false
34800          */
34801         "visibilitychange" : true,
34802         /**
34803          * @event paneladded
34804          * Fires when a panel is added. 
34805          * @param {Roo.LayoutRegion} this
34806          * @param {Roo.ContentPanel} panel The panel
34807          */
34808         "paneladded" : true,
34809         /**
34810          * @event panelremoved
34811          * Fires when a panel is removed. 
34812          * @param {Roo.LayoutRegion} this
34813          * @param {Roo.ContentPanel} panel The panel
34814          */
34815         "panelremoved" : true,
34816         /**
34817          * @event beforecollapse
34818          * Fires when this region before collapse.
34819          * @param {Roo.LayoutRegion} this
34820          */
34821         "beforecollapse" : true,
34822         /**
34823          * @event collapsed
34824          * Fires when this region is collapsed.
34825          * @param {Roo.LayoutRegion} this
34826          */
34827         "collapsed" : true,
34828         /**
34829          * @event expanded
34830          * Fires when this region is expanded.
34831          * @param {Roo.LayoutRegion} this
34832          */
34833         "expanded" : true,
34834         /**
34835          * @event slideshow
34836          * Fires when this region is slid into view.
34837          * @param {Roo.LayoutRegion} this
34838          */
34839         "slideshow" : true,
34840         /**
34841          * @event slidehide
34842          * Fires when this region slides out of view. 
34843          * @param {Roo.LayoutRegion} this
34844          */
34845         "slidehide" : true,
34846         /**
34847          * @event panelactivated
34848          * Fires when a panel is activated. 
34849          * @param {Roo.LayoutRegion} this
34850          * @param {Roo.ContentPanel} panel The activated panel
34851          */
34852         "panelactivated" : true,
34853         /**
34854          * @event resized
34855          * Fires when the user resizes this region. 
34856          * @param {Roo.LayoutRegion} this
34857          * @param {Number} newSize The new size (width for east/west, height for north/south)
34858          */
34859         "resized" : true
34860     };
34861     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34862     this.panels = new Roo.util.MixedCollection();
34863     this.panels.getKey = this.getPanelId.createDelegate(this);
34864     this.box = null;
34865     this.activePanel = null;
34866     // ensure listeners are added...
34867     
34868     if (config.listeners || config.events) {
34869         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34870             listeners : config.listeners || {},
34871             events : config.events || {}
34872         });
34873     }
34874     
34875     if(skipConfig !== true){
34876         this.applyConfig(config);
34877     }
34878 };
34879
34880 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34881 {
34882     getPanelId : function(p){
34883         return p.getId();
34884     },
34885     
34886     applyConfig : function(config){
34887         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34888         this.config = config;
34889         
34890     },
34891     
34892     /**
34893      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34894      * the width, for horizontal (north, south) the height.
34895      * @param {Number} newSize The new width or height
34896      */
34897     resizeTo : function(newSize){
34898         var el = this.el ? this.el :
34899                  (this.activePanel ? this.activePanel.getEl() : null);
34900         if(el){
34901             switch(this.position){
34902                 case "east":
34903                 case "west":
34904                     el.setWidth(newSize);
34905                     this.fireEvent("resized", this, newSize);
34906                 break;
34907                 case "north":
34908                 case "south":
34909                     el.setHeight(newSize);
34910                     this.fireEvent("resized", this, newSize);
34911                 break;                
34912             }
34913         }
34914     },
34915     
34916     getBox : function(){
34917         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34918     },
34919     
34920     getMargins : function(){
34921         return this.margins;
34922     },
34923     
34924     updateBox : function(box){
34925         this.box = box;
34926         var el = this.activePanel.getEl();
34927         el.dom.style.left = box.x + "px";
34928         el.dom.style.top = box.y + "px";
34929         this.activePanel.setSize(box.width, box.height);
34930     },
34931     
34932     /**
34933      * Returns the container element for this region.
34934      * @return {Roo.Element}
34935      */
34936     getEl : function(){
34937         return this.activePanel;
34938     },
34939     
34940     /**
34941      * Returns true if this region is currently visible.
34942      * @return {Boolean}
34943      */
34944     isVisible : function(){
34945         return this.activePanel ? true : false;
34946     },
34947     
34948     setActivePanel : function(panel){
34949         panel = this.getPanel(panel);
34950         if(this.activePanel && this.activePanel != panel){
34951             this.activePanel.setActiveState(false);
34952             this.activePanel.getEl().setLeftTop(-10000,-10000);
34953         }
34954         this.activePanel = panel;
34955         panel.setActiveState(true);
34956         if(this.box){
34957             panel.setSize(this.box.width, this.box.height);
34958         }
34959         this.fireEvent("panelactivated", this, panel);
34960         this.fireEvent("invalidated");
34961     },
34962     
34963     /**
34964      * Show the specified panel.
34965      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34966      * @return {Roo.ContentPanel} The shown panel or null
34967      */
34968     showPanel : function(panel){
34969         panel = this.getPanel(panel);
34970         if(panel){
34971             this.setActivePanel(panel);
34972         }
34973         return panel;
34974     },
34975     
34976     /**
34977      * Get the active panel for this region.
34978      * @return {Roo.ContentPanel} The active panel or null
34979      */
34980     getActivePanel : function(){
34981         return this.activePanel;
34982     },
34983     
34984     /**
34985      * Add the passed ContentPanel(s)
34986      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34987      * @return {Roo.ContentPanel} The panel added (if only one was added)
34988      */
34989     add : function(panel){
34990         if(arguments.length > 1){
34991             for(var i = 0, len = arguments.length; i < len; i++) {
34992                 this.add(arguments[i]);
34993             }
34994             return null;
34995         }
34996         if(this.hasPanel(panel)){
34997             this.showPanel(panel);
34998             return panel;
34999         }
35000         var el = panel.getEl();
35001         if(el.dom.parentNode != this.mgr.el.dom){
35002             this.mgr.el.dom.appendChild(el.dom);
35003         }
35004         if(panel.setRegion){
35005             panel.setRegion(this);
35006         }
35007         this.panels.add(panel);
35008         el.setStyle("position", "absolute");
35009         if(!panel.background){
35010             this.setActivePanel(panel);
35011             if(this.config.initialSize && this.panels.getCount()==1){
35012                 this.resizeTo(this.config.initialSize);
35013             }
35014         }
35015         this.fireEvent("paneladded", this, panel);
35016         return panel;
35017     },
35018     
35019     /**
35020      * Returns true if the panel is in this region.
35021      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35022      * @return {Boolean}
35023      */
35024     hasPanel : function(panel){
35025         if(typeof panel == "object"){ // must be panel obj
35026             panel = panel.getId();
35027         }
35028         return this.getPanel(panel) ? true : false;
35029     },
35030     
35031     /**
35032      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35033      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35034      * @param {Boolean} preservePanel Overrides the config preservePanel option
35035      * @return {Roo.ContentPanel} The panel that was removed
35036      */
35037     remove : function(panel, preservePanel){
35038         panel = this.getPanel(panel);
35039         if(!panel){
35040             return null;
35041         }
35042         var e = {};
35043         this.fireEvent("beforeremove", this, panel, e);
35044         if(e.cancel === true){
35045             return null;
35046         }
35047         var panelId = panel.getId();
35048         this.panels.removeKey(panelId);
35049         return panel;
35050     },
35051     
35052     /**
35053      * Returns the panel specified or null if it's not in this region.
35054      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35055      * @return {Roo.ContentPanel}
35056      */
35057     getPanel : function(id){
35058         if(typeof id == "object"){ // must be panel obj
35059             return id;
35060         }
35061         return this.panels.get(id);
35062     },
35063     
35064     /**
35065      * Returns this regions position (north/south/east/west/center).
35066      * @return {String} 
35067      */
35068     getPosition: function(){
35069         return this.position;    
35070     }
35071 });/*
35072  * Based on:
35073  * Ext JS Library 1.1.1
35074  * Copyright(c) 2006-2007, Ext JS, LLC.
35075  *
35076  * Originally Released Under LGPL - original licence link has changed is not relivant.
35077  *
35078  * Fork - LGPL
35079  * <script type="text/javascript">
35080  */
35081  
35082 /**
35083  * @class Roo.bootstrap.layout.Region
35084  * @extends Roo.bootstrap.layout.Basic
35085  * This class represents a region in a layout manager.
35086  
35087  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35088  * @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})
35089  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35090  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35091  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35092  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35093  * @cfg {String}    title           The title for the region (overrides panel titles)
35094  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35095  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35096  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35097  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35098  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35099  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35100  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35101  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35102  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35103  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35104
35105  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35106  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35107  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35108  * @cfg {Number}    width           For East/West panels
35109  * @cfg {Number}    height          For North/South panels
35110  * @cfg {Boolean}   split           To show the splitter
35111  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35112  * 
35113  * @cfg {string}   cls             Extra CSS classes to add to region
35114  * 
35115  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35116  * @cfg {string}   region  the region that it inhabits..
35117  *
35118
35119  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35120  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35121
35122  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35123  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35124  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35125  */
35126 Roo.bootstrap.layout.Region = function(config)
35127 {
35128     this.applyConfig(config);
35129
35130     var mgr = config.mgr;
35131     var pos = config.region;
35132     config.skipConfig = true;
35133     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35134     
35135     if (mgr.el) {
35136         this.onRender(mgr.el);   
35137     }
35138      
35139     this.visible = true;
35140     this.collapsed = false;
35141     this.unrendered_panels = [];
35142 };
35143
35144 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35145
35146     position: '', // set by wrapper (eg. north/south etc..)
35147     unrendered_panels : null,  // unrendered panels.
35148     createBody : function(){
35149         /** This region's body element 
35150         * @type Roo.Element */
35151         this.bodyEl = this.el.createChild({
35152                 tag: "div",
35153                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35154         });
35155     },
35156
35157     onRender: function(ctr, pos)
35158     {
35159         var dh = Roo.DomHelper;
35160         /** This region's container element 
35161         * @type Roo.Element */
35162         this.el = dh.append(ctr.dom, {
35163                 tag: "div",
35164                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35165             }, true);
35166         /** This region's title element 
35167         * @type Roo.Element */
35168     
35169         this.titleEl = dh.append(this.el.dom,
35170             {
35171                     tag: "div",
35172                     unselectable: "on",
35173                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35174                     children:[
35175                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35176                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35177                     ]}, true);
35178         
35179         this.titleEl.enableDisplayMode();
35180         /** This region's title text element 
35181         * @type HTMLElement */
35182         this.titleTextEl = this.titleEl.dom.firstChild;
35183         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35184         /*
35185         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35186         this.closeBtn.enableDisplayMode();
35187         this.closeBtn.on("click", this.closeClicked, this);
35188         this.closeBtn.hide();
35189     */
35190         this.createBody(this.config);
35191         if(this.config.hideWhenEmpty){
35192             this.hide();
35193             this.on("paneladded", this.validateVisibility, this);
35194             this.on("panelremoved", this.validateVisibility, this);
35195         }
35196         if(this.autoScroll){
35197             this.bodyEl.setStyle("overflow", "auto");
35198         }else{
35199             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35200         }
35201         //if(c.titlebar !== false){
35202             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35203                 this.titleEl.hide();
35204             }else{
35205                 this.titleEl.show();
35206                 if(this.config.title){
35207                     this.titleTextEl.innerHTML = this.config.title;
35208                 }
35209             }
35210         //}
35211         if(this.config.collapsed){
35212             this.collapse(true);
35213         }
35214         if(this.config.hidden){
35215             this.hide();
35216         }
35217         
35218         if (this.unrendered_panels && this.unrendered_panels.length) {
35219             for (var i =0;i< this.unrendered_panels.length; i++) {
35220                 this.add(this.unrendered_panels[i]);
35221             }
35222             this.unrendered_panels = null;
35223             
35224         }
35225         
35226     },
35227     
35228     applyConfig : function(c)
35229     {
35230         /*
35231          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35232             var dh = Roo.DomHelper;
35233             if(c.titlebar !== false){
35234                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35235                 this.collapseBtn.on("click", this.collapse, this);
35236                 this.collapseBtn.enableDisplayMode();
35237                 /*
35238                 if(c.showPin === true || this.showPin){
35239                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35240                     this.stickBtn.enableDisplayMode();
35241                     this.stickBtn.on("click", this.expand, this);
35242                     this.stickBtn.hide();
35243                 }
35244                 
35245             }
35246             */
35247             /** This region's collapsed element
35248             * @type Roo.Element */
35249             /*
35250              *
35251             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35252                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35253             ]}, true);
35254             
35255             if(c.floatable !== false){
35256                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35257                this.collapsedEl.on("click", this.collapseClick, this);
35258             }
35259
35260             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35261                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35262                    id: "message", unselectable: "on", style:{"float":"left"}});
35263                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35264              }
35265             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35266             this.expandBtn.on("click", this.expand, this);
35267             
35268         }
35269         
35270         if(this.collapseBtn){
35271             this.collapseBtn.setVisible(c.collapsible == true);
35272         }
35273         
35274         this.cmargins = c.cmargins || this.cmargins ||
35275                          (this.position == "west" || this.position == "east" ?
35276                              {top: 0, left: 2, right:2, bottom: 0} :
35277                              {top: 2, left: 0, right:0, bottom: 2});
35278         */
35279         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35280         
35281         
35282         this.bottomTabs = c.tabPosition != "top";
35283         
35284         this.autoScroll = c.autoScroll || false;
35285         
35286         
35287        
35288         
35289         this.duration = c.duration || .30;
35290         this.slideDuration = c.slideDuration || .45;
35291         this.config = c;
35292        
35293     },
35294     /**
35295      * Returns true if this region is currently visible.
35296      * @return {Boolean}
35297      */
35298     isVisible : function(){
35299         return this.visible;
35300     },
35301
35302     /**
35303      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35304      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35305      */
35306     //setCollapsedTitle : function(title){
35307     //    title = title || "&#160;";
35308      //   if(this.collapsedTitleTextEl){
35309       //      this.collapsedTitleTextEl.innerHTML = title;
35310        // }
35311     //},
35312
35313     getBox : function(){
35314         var b;
35315       //  if(!this.collapsed){
35316             b = this.el.getBox(false, true);
35317        // }else{
35318           //  b = this.collapsedEl.getBox(false, true);
35319         //}
35320         return b;
35321     },
35322
35323     getMargins : function(){
35324         return this.margins;
35325         //return this.collapsed ? this.cmargins : this.margins;
35326     },
35327 /*
35328     highlight : function(){
35329         this.el.addClass("x-layout-panel-dragover");
35330     },
35331
35332     unhighlight : function(){
35333         this.el.removeClass("x-layout-panel-dragover");
35334     },
35335 */
35336     updateBox : function(box)
35337     {
35338         if (!this.bodyEl) {
35339             return; // not rendered yet..
35340         }
35341         
35342         this.box = box;
35343         if(!this.collapsed){
35344             this.el.dom.style.left = box.x + "px";
35345             this.el.dom.style.top = box.y + "px";
35346             this.updateBody(box.width, box.height);
35347         }else{
35348             this.collapsedEl.dom.style.left = box.x + "px";
35349             this.collapsedEl.dom.style.top = box.y + "px";
35350             this.collapsedEl.setSize(box.width, box.height);
35351         }
35352         if(this.tabs){
35353             this.tabs.autoSizeTabs();
35354         }
35355     },
35356
35357     updateBody : function(w, h)
35358     {
35359         if(w !== null){
35360             this.el.setWidth(w);
35361             w -= this.el.getBorderWidth("rl");
35362             if(this.config.adjustments){
35363                 w += this.config.adjustments[0];
35364             }
35365         }
35366         if(h !== null && h > 0){
35367             this.el.setHeight(h);
35368             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35369             h -= this.el.getBorderWidth("tb");
35370             if(this.config.adjustments){
35371                 h += this.config.adjustments[1];
35372             }
35373             this.bodyEl.setHeight(h);
35374             if(this.tabs){
35375                 h = this.tabs.syncHeight(h);
35376             }
35377         }
35378         if(this.panelSize){
35379             w = w !== null ? w : this.panelSize.width;
35380             h = h !== null ? h : this.panelSize.height;
35381         }
35382         if(this.activePanel){
35383             var el = this.activePanel.getEl();
35384             w = w !== null ? w : el.getWidth();
35385             h = h !== null ? h : el.getHeight();
35386             this.panelSize = {width: w, height: h};
35387             this.activePanel.setSize(w, h);
35388         }
35389         if(Roo.isIE && this.tabs){
35390             this.tabs.el.repaint();
35391         }
35392     },
35393
35394     /**
35395      * Returns the container element for this region.
35396      * @return {Roo.Element}
35397      */
35398     getEl : function(){
35399         return this.el;
35400     },
35401
35402     /**
35403      * Hides this region.
35404      */
35405     hide : function(){
35406         //if(!this.collapsed){
35407             this.el.dom.style.left = "-2000px";
35408             this.el.hide();
35409         //}else{
35410          //   this.collapsedEl.dom.style.left = "-2000px";
35411          //   this.collapsedEl.hide();
35412        // }
35413         this.visible = false;
35414         this.fireEvent("visibilitychange", this, false);
35415     },
35416
35417     /**
35418      * Shows this region if it was previously hidden.
35419      */
35420     show : function(){
35421         //if(!this.collapsed){
35422             this.el.show();
35423         //}else{
35424         //    this.collapsedEl.show();
35425        // }
35426         this.visible = true;
35427         this.fireEvent("visibilitychange", this, true);
35428     },
35429 /*
35430     closeClicked : function(){
35431         if(this.activePanel){
35432             this.remove(this.activePanel);
35433         }
35434     },
35435
35436     collapseClick : function(e){
35437         if(this.isSlid){
35438            e.stopPropagation();
35439            this.slideIn();
35440         }else{
35441            e.stopPropagation();
35442            this.slideOut();
35443         }
35444     },
35445 */
35446     /**
35447      * Collapses this region.
35448      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35449      */
35450     /*
35451     collapse : function(skipAnim, skipCheck = false){
35452         if(this.collapsed) {
35453             return;
35454         }
35455         
35456         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35457             
35458             this.collapsed = true;
35459             if(this.split){
35460                 this.split.el.hide();
35461             }
35462             if(this.config.animate && skipAnim !== true){
35463                 this.fireEvent("invalidated", this);
35464                 this.animateCollapse();
35465             }else{
35466                 this.el.setLocation(-20000,-20000);
35467                 this.el.hide();
35468                 this.collapsedEl.show();
35469                 this.fireEvent("collapsed", this);
35470                 this.fireEvent("invalidated", this);
35471             }
35472         }
35473         
35474     },
35475 */
35476     animateCollapse : function(){
35477         // overridden
35478     },
35479
35480     /**
35481      * Expands this region if it was previously collapsed.
35482      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35483      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35484      */
35485     /*
35486     expand : function(e, skipAnim){
35487         if(e) {
35488             e.stopPropagation();
35489         }
35490         if(!this.collapsed || this.el.hasActiveFx()) {
35491             return;
35492         }
35493         if(this.isSlid){
35494             this.afterSlideIn();
35495             skipAnim = true;
35496         }
35497         this.collapsed = false;
35498         if(this.config.animate && skipAnim !== true){
35499             this.animateExpand();
35500         }else{
35501             this.el.show();
35502             if(this.split){
35503                 this.split.el.show();
35504             }
35505             this.collapsedEl.setLocation(-2000,-2000);
35506             this.collapsedEl.hide();
35507             this.fireEvent("invalidated", this);
35508             this.fireEvent("expanded", this);
35509         }
35510     },
35511 */
35512     animateExpand : function(){
35513         // overridden
35514     },
35515
35516     initTabs : function()
35517     {
35518         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35519         
35520         var ts = new Roo.bootstrap.panel.Tabs({
35521                 el: this.bodyEl.dom,
35522                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35523                 disableTooltips: this.config.disableTabTips,
35524                 toolbar : this.config.toolbar
35525             });
35526         
35527         if(this.config.hideTabs){
35528             ts.stripWrap.setDisplayed(false);
35529         }
35530         this.tabs = ts;
35531         ts.resizeTabs = this.config.resizeTabs === true;
35532         ts.minTabWidth = this.config.minTabWidth || 40;
35533         ts.maxTabWidth = this.config.maxTabWidth || 250;
35534         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35535         ts.monitorResize = false;
35536         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35537         ts.bodyEl.addClass('roo-layout-tabs-body');
35538         this.panels.each(this.initPanelAsTab, this);
35539     },
35540
35541     initPanelAsTab : function(panel){
35542         var ti = this.tabs.addTab(
35543             panel.getEl().id,
35544             panel.getTitle(),
35545             null,
35546             this.config.closeOnTab && panel.isClosable(),
35547             panel.tpl
35548         );
35549         if(panel.tabTip !== undefined){
35550             ti.setTooltip(panel.tabTip);
35551         }
35552         ti.on("activate", function(){
35553               this.setActivePanel(panel);
35554         }, this);
35555         
35556         if(this.config.closeOnTab){
35557             ti.on("beforeclose", function(t, e){
35558                 e.cancel = true;
35559                 this.remove(panel);
35560             }, this);
35561         }
35562         
35563         panel.tabItem = ti;
35564         
35565         return ti;
35566     },
35567
35568     updatePanelTitle : function(panel, title)
35569     {
35570         if(this.activePanel == panel){
35571             this.updateTitle(title);
35572         }
35573         if(this.tabs){
35574             var ti = this.tabs.getTab(panel.getEl().id);
35575             ti.setText(title);
35576             if(panel.tabTip !== undefined){
35577                 ti.setTooltip(panel.tabTip);
35578             }
35579         }
35580     },
35581
35582     updateTitle : function(title){
35583         if(this.titleTextEl && !this.config.title){
35584             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35585         }
35586     },
35587
35588     setActivePanel : function(panel)
35589     {
35590         panel = this.getPanel(panel);
35591         if(this.activePanel && this.activePanel != panel){
35592             if(this.activePanel.setActiveState(false) === false){
35593                 return;
35594             }
35595         }
35596         this.activePanel = panel;
35597         panel.setActiveState(true);
35598         if(this.panelSize){
35599             panel.setSize(this.panelSize.width, this.panelSize.height);
35600         }
35601         if(this.closeBtn){
35602             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35603         }
35604         this.updateTitle(panel.getTitle());
35605         if(this.tabs){
35606             this.fireEvent("invalidated", this);
35607         }
35608         this.fireEvent("panelactivated", this, panel);
35609     },
35610
35611     /**
35612      * Shows the specified panel.
35613      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35614      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35615      */
35616     showPanel : function(panel)
35617     {
35618         panel = this.getPanel(panel);
35619         if(panel){
35620             if(this.tabs){
35621                 var tab = this.tabs.getTab(panel.getEl().id);
35622                 if(tab.isHidden()){
35623                     this.tabs.unhideTab(tab.id);
35624                 }
35625                 tab.activate();
35626             }else{
35627                 this.setActivePanel(panel);
35628             }
35629         }
35630         return panel;
35631     },
35632
35633     /**
35634      * Get the active panel for this region.
35635      * @return {Roo.ContentPanel} The active panel or null
35636      */
35637     getActivePanel : function(){
35638         return this.activePanel;
35639     },
35640
35641     validateVisibility : function(){
35642         if(this.panels.getCount() < 1){
35643             this.updateTitle("&#160;");
35644             this.closeBtn.hide();
35645             this.hide();
35646         }else{
35647             if(!this.isVisible()){
35648                 this.show();
35649             }
35650         }
35651     },
35652
35653     /**
35654      * Adds the passed ContentPanel(s) to this region.
35655      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35656      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35657      */
35658     add : function(panel)
35659     {
35660         if(arguments.length > 1){
35661             for(var i = 0, len = arguments.length; i < len; i++) {
35662                 this.add(arguments[i]);
35663             }
35664             return null;
35665         }
35666         
35667         // if we have not been rendered yet, then we can not really do much of this..
35668         if (!this.bodyEl) {
35669             this.unrendered_panels.push(panel);
35670             return panel;
35671         }
35672         
35673         
35674         
35675         
35676         if(this.hasPanel(panel)){
35677             this.showPanel(panel);
35678             return panel;
35679         }
35680         panel.setRegion(this);
35681         this.panels.add(panel);
35682        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35683             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35684             // and hide them... ???
35685             this.bodyEl.dom.appendChild(panel.getEl().dom);
35686             if(panel.background !== true){
35687                 this.setActivePanel(panel);
35688             }
35689             this.fireEvent("paneladded", this, panel);
35690             return panel;
35691         }
35692         */
35693         if(!this.tabs){
35694             this.initTabs();
35695         }else{
35696             this.initPanelAsTab(panel);
35697         }
35698         
35699         
35700         if(panel.background !== true){
35701             this.tabs.activate(panel.getEl().id);
35702         }
35703         this.fireEvent("paneladded", this, panel);
35704         return panel;
35705     },
35706
35707     /**
35708      * Hides the tab for the specified panel.
35709      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35710      */
35711     hidePanel : function(panel){
35712         if(this.tabs && (panel = this.getPanel(panel))){
35713             this.tabs.hideTab(panel.getEl().id);
35714         }
35715     },
35716
35717     /**
35718      * Unhides the tab for a previously hidden panel.
35719      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35720      */
35721     unhidePanel : function(panel){
35722         if(this.tabs && (panel = this.getPanel(panel))){
35723             this.tabs.unhideTab(panel.getEl().id);
35724         }
35725     },
35726
35727     clearPanels : function(){
35728         while(this.panels.getCount() > 0){
35729              this.remove(this.panels.first());
35730         }
35731     },
35732
35733     /**
35734      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35735      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35736      * @param {Boolean} preservePanel Overrides the config preservePanel option
35737      * @return {Roo.ContentPanel} The panel that was removed
35738      */
35739     remove : function(panel, preservePanel)
35740     {
35741         panel = this.getPanel(panel);
35742         if(!panel){
35743             return null;
35744         }
35745         var e = {};
35746         this.fireEvent("beforeremove", this, panel, e);
35747         if(e.cancel === true){
35748             return null;
35749         }
35750         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35751         var panelId = panel.getId();
35752         this.panels.removeKey(panelId);
35753         if(preservePanel){
35754             document.body.appendChild(panel.getEl().dom);
35755         }
35756         if(this.tabs){
35757             this.tabs.removeTab(panel.getEl().id);
35758         }else if (!preservePanel){
35759             this.bodyEl.dom.removeChild(panel.getEl().dom);
35760         }
35761         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35762             var p = this.panels.first();
35763             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35764             tempEl.appendChild(p.getEl().dom);
35765             this.bodyEl.update("");
35766             this.bodyEl.dom.appendChild(p.getEl().dom);
35767             tempEl = null;
35768             this.updateTitle(p.getTitle());
35769             this.tabs = null;
35770             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35771             this.setActivePanel(p);
35772         }
35773         panel.setRegion(null);
35774         if(this.activePanel == panel){
35775             this.activePanel = null;
35776         }
35777         if(this.config.autoDestroy !== false && preservePanel !== true){
35778             try{panel.destroy();}catch(e){}
35779         }
35780         this.fireEvent("panelremoved", this, panel);
35781         return panel;
35782     },
35783
35784     /**
35785      * Returns the TabPanel component used by this region
35786      * @return {Roo.TabPanel}
35787      */
35788     getTabs : function(){
35789         return this.tabs;
35790     },
35791
35792     createTool : function(parentEl, className){
35793         var btn = Roo.DomHelper.append(parentEl, {
35794             tag: "div",
35795             cls: "x-layout-tools-button",
35796             children: [ {
35797                 tag: "div",
35798                 cls: "roo-layout-tools-button-inner " + className,
35799                 html: "&#160;"
35800             }]
35801         }, true);
35802         btn.addClassOnOver("roo-layout-tools-button-over");
35803         return btn;
35804     }
35805 });/*
35806  * Based on:
35807  * Ext JS Library 1.1.1
35808  * Copyright(c) 2006-2007, Ext JS, LLC.
35809  *
35810  * Originally Released Under LGPL - original licence link has changed is not relivant.
35811  *
35812  * Fork - LGPL
35813  * <script type="text/javascript">
35814  */
35815  
35816
35817
35818 /**
35819  * @class Roo.SplitLayoutRegion
35820  * @extends Roo.LayoutRegion
35821  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35822  */
35823 Roo.bootstrap.layout.Split = function(config){
35824     this.cursor = config.cursor;
35825     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35826 };
35827
35828 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35829 {
35830     splitTip : "Drag to resize.",
35831     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35832     useSplitTips : false,
35833
35834     applyConfig : function(config){
35835         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35836     },
35837     
35838     onRender : function(ctr,pos) {
35839         
35840         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35841         if(!this.config.split){
35842             return;
35843         }
35844         if(!this.split){
35845             
35846             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35847                             tag: "div",
35848                             id: this.el.id + "-split",
35849                             cls: "roo-layout-split roo-layout-split-"+this.position,
35850                             html: "&#160;"
35851             });
35852             /** The SplitBar for this region 
35853             * @type Roo.SplitBar */
35854             // does not exist yet...
35855             Roo.log([this.position, this.orientation]);
35856             
35857             this.split = new Roo.bootstrap.SplitBar({
35858                 dragElement : splitEl,
35859                 resizingElement: this.el,
35860                 orientation : this.orientation
35861             });
35862             
35863             this.split.on("moved", this.onSplitMove, this);
35864             this.split.useShim = this.config.useShim === true;
35865             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35866             if(this.useSplitTips){
35867                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35868             }
35869             //if(config.collapsible){
35870             //    this.split.el.on("dblclick", this.collapse,  this);
35871             //}
35872         }
35873         if(typeof this.config.minSize != "undefined"){
35874             this.split.minSize = this.config.minSize;
35875         }
35876         if(typeof this.config.maxSize != "undefined"){
35877             this.split.maxSize = this.config.maxSize;
35878         }
35879         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35880             this.hideSplitter();
35881         }
35882         
35883     },
35884
35885     getHMaxSize : function(){
35886          var cmax = this.config.maxSize || 10000;
35887          var center = this.mgr.getRegion("center");
35888          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35889     },
35890
35891     getVMaxSize : function(){
35892          var cmax = this.config.maxSize || 10000;
35893          var center = this.mgr.getRegion("center");
35894          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35895     },
35896
35897     onSplitMove : function(split, newSize){
35898         this.fireEvent("resized", this, newSize);
35899     },
35900     
35901     /** 
35902      * Returns the {@link Roo.SplitBar} for this region.
35903      * @return {Roo.SplitBar}
35904      */
35905     getSplitBar : function(){
35906         return this.split;
35907     },
35908     
35909     hide : function(){
35910         this.hideSplitter();
35911         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35912     },
35913
35914     hideSplitter : function(){
35915         if(this.split){
35916             this.split.el.setLocation(-2000,-2000);
35917             this.split.el.hide();
35918         }
35919     },
35920
35921     show : function(){
35922         if(this.split){
35923             this.split.el.show();
35924         }
35925         Roo.bootstrap.layout.Split.superclass.show.call(this);
35926     },
35927     
35928     beforeSlide: function(){
35929         if(Roo.isGecko){// firefox overflow auto bug workaround
35930             this.bodyEl.clip();
35931             if(this.tabs) {
35932                 this.tabs.bodyEl.clip();
35933             }
35934             if(this.activePanel){
35935                 this.activePanel.getEl().clip();
35936                 
35937                 if(this.activePanel.beforeSlide){
35938                     this.activePanel.beforeSlide();
35939                 }
35940             }
35941         }
35942     },
35943     
35944     afterSlide : function(){
35945         if(Roo.isGecko){// firefox overflow auto bug workaround
35946             this.bodyEl.unclip();
35947             if(this.tabs) {
35948                 this.tabs.bodyEl.unclip();
35949             }
35950             if(this.activePanel){
35951                 this.activePanel.getEl().unclip();
35952                 if(this.activePanel.afterSlide){
35953                     this.activePanel.afterSlide();
35954                 }
35955             }
35956         }
35957     },
35958
35959     initAutoHide : function(){
35960         if(this.autoHide !== false){
35961             if(!this.autoHideHd){
35962                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35963                 this.autoHideHd = {
35964                     "mouseout": function(e){
35965                         if(!e.within(this.el, true)){
35966                             st.delay(500);
35967                         }
35968                     },
35969                     "mouseover" : function(e){
35970                         st.cancel();
35971                     },
35972                     scope : this
35973                 };
35974             }
35975             this.el.on(this.autoHideHd);
35976         }
35977     },
35978
35979     clearAutoHide : function(){
35980         if(this.autoHide !== false){
35981             this.el.un("mouseout", this.autoHideHd.mouseout);
35982             this.el.un("mouseover", this.autoHideHd.mouseover);
35983         }
35984     },
35985
35986     clearMonitor : function(){
35987         Roo.get(document).un("click", this.slideInIf, this);
35988     },
35989
35990     // these names are backwards but not changed for compat
35991     slideOut : function(){
35992         if(this.isSlid || this.el.hasActiveFx()){
35993             return;
35994         }
35995         this.isSlid = true;
35996         if(this.collapseBtn){
35997             this.collapseBtn.hide();
35998         }
35999         this.closeBtnState = this.closeBtn.getStyle('display');
36000         this.closeBtn.hide();
36001         if(this.stickBtn){
36002             this.stickBtn.show();
36003         }
36004         this.el.show();
36005         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36006         this.beforeSlide();
36007         this.el.setStyle("z-index", 10001);
36008         this.el.slideIn(this.getSlideAnchor(), {
36009             callback: function(){
36010                 this.afterSlide();
36011                 this.initAutoHide();
36012                 Roo.get(document).on("click", this.slideInIf, this);
36013                 this.fireEvent("slideshow", this);
36014             },
36015             scope: this,
36016             block: true
36017         });
36018     },
36019
36020     afterSlideIn : function(){
36021         this.clearAutoHide();
36022         this.isSlid = false;
36023         this.clearMonitor();
36024         this.el.setStyle("z-index", "");
36025         if(this.collapseBtn){
36026             this.collapseBtn.show();
36027         }
36028         this.closeBtn.setStyle('display', this.closeBtnState);
36029         if(this.stickBtn){
36030             this.stickBtn.hide();
36031         }
36032         this.fireEvent("slidehide", this);
36033     },
36034
36035     slideIn : function(cb){
36036         if(!this.isSlid || this.el.hasActiveFx()){
36037             Roo.callback(cb);
36038             return;
36039         }
36040         this.isSlid = false;
36041         this.beforeSlide();
36042         this.el.slideOut(this.getSlideAnchor(), {
36043             callback: function(){
36044                 this.el.setLeftTop(-10000, -10000);
36045                 this.afterSlide();
36046                 this.afterSlideIn();
36047                 Roo.callback(cb);
36048             },
36049             scope: this,
36050             block: true
36051         });
36052     },
36053     
36054     slideInIf : function(e){
36055         if(!e.within(this.el)){
36056             this.slideIn();
36057         }
36058     },
36059
36060     animateCollapse : function(){
36061         this.beforeSlide();
36062         this.el.setStyle("z-index", 20000);
36063         var anchor = this.getSlideAnchor();
36064         this.el.slideOut(anchor, {
36065             callback : function(){
36066                 this.el.setStyle("z-index", "");
36067                 this.collapsedEl.slideIn(anchor, {duration:.3});
36068                 this.afterSlide();
36069                 this.el.setLocation(-10000,-10000);
36070                 this.el.hide();
36071                 this.fireEvent("collapsed", this);
36072             },
36073             scope: this,
36074             block: true
36075         });
36076     },
36077
36078     animateExpand : function(){
36079         this.beforeSlide();
36080         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36081         this.el.setStyle("z-index", 20000);
36082         this.collapsedEl.hide({
36083             duration:.1
36084         });
36085         this.el.slideIn(this.getSlideAnchor(), {
36086             callback : function(){
36087                 this.el.setStyle("z-index", "");
36088                 this.afterSlide();
36089                 if(this.split){
36090                     this.split.el.show();
36091                 }
36092                 this.fireEvent("invalidated", this);
36093                 this.fireEvent("expanded", this);
36094             },
36095             scope: this,
36096             block: true
36097         });
36098     },
36099
36100     anchors : {
36101         "west" : "left",
36102         "east" : "right",
36103         "north" : "top",
36104         "south" : "bottom"
36105     },
36106
36107     sanchors : {
36108         "west" : "l",
36109         "east" : "r",
36110         "north" : "t",
36111         "south" : "b"
36112     },
36113
36114     canchors : {
36115         "west" : "tl-tr",
36116         "east" : "tr-tl",
36117         "north" : "tl-bl",
36118         "south" : "bl-tl"
36119     },
36120
36121     getAnchor : function(){
36122         return this.anchors[this.position];
36123     },
36124
36125     getCollapseAnchor : function(){
36126         return this.canchors[this.position];
36127     },
36128
36129     getSlideAnchor : function(){
36130         return this.sanchors[this.position];
36131     },
36132
36133     getAlignAdj : function(){
36134         var cm = this.cmargins;
36135         switch(this.position){
36136             case "west":
36137                 return [0, 0];
36138             break;
36139             case "east":
36140                 return [0, 0];
36141             break;
36142             case "north":
36143                 return [0, 0];
36144             break;
36145             case "south":
36146                 return [0, 0];
36147             break;
36148         }
36149     },
36150
36151     getExpandAdj : function(){
36152         var c = this.collapsedEl, cm = this.cmargins;
36153         switch(this.position){
36154             case "west":
36155                 return [-(cm.right+c.getWidth()+cm.left), 0];
36156             break;
36157             case "east":
36158                 return [cm.right+c.getWidth()+cm.left, 0];
36159             break;
36160             case "north":
36161                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36162             break;
36163             case "south":
36164                 return [0, cm.top+cm.bottom+c.getHeight()];
36165             break;
36166         }
36167     }
36168 });/*
36169  * Based on:
36170  * Ext JS Library 1.1.1
36171  * Copyright(c) 2006-2007, Ext JS, LLC.
36172  *
36173  * Originally Released Under LGPL - original licence link has changed is not relivant.
36174  *
36175  * Fork - LGPL
36176  * <script type="text/javascript">
36177  */
36178 /*
36179  * These classes are private internal classes
36180  */
36181 Roo.bootstrap.layout.Center = function(config){
36182     config.region = "center";
36183     Roo.bootstrap.layout.Region.call(this, config);
36184     this.visible = true;
36185     this.minWidth = config.minWidth || 20;
36186     this.minHeight = config.minHeight || 20;
36187 };
36188
36189 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36190     hide : function(){
36191         // center panel can't be hidden
36192     },
36193     
36194     show : function(){
36195         // center panel can't be hidden
36196     },
36197     
36198     getMinWidth: function(){
36199         return this.minWidth;
36200     },
36201     
36202     getMinHeight: function(){
36203         return this.minHeight;
36204     }
36205 });
36206
36207
36208
36209
36210  
36211
36212
36213
36214
36215
36216 Roo.bootstrap.layout.North = function(config)
36217 {
36218     config.region = 'north';
36219     config.cursor = 'n-resize';
36220     
36221     Roo.bootstrap.layout.Split.call(this, config);
36222     
36223     
36224     if(this.split){
36225         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36226         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36227         this.split.el.addClass("roo-layout-split-v");
36228     }
36229     var size = config.initialSize || config.height;
36230     if(typeof size != "undefined"){
36231         this.el.setHeight(size);
36232     }
36233 };
36234 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36235 {
36236     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36237     
36238     
36239     
36240     getBox : function(){
36241         if(this.collapsed){
36242             return this.collapsedEl.getBox();
36243         }
36244         var box = this.el.getBox();
36245         if(this.split){
36246             box.height += this.split.el.getHeight();
36247         }
36248         return box;
36249     },
36250     
36251     updateBox : function(box){
36252         if(this.split && !this.collapsed){
36253             box.height -= this.split.el.getHeight();
36254             this.split.el.setLeft(box.x);
36255             this.split.el.setTop(box.y+box.height);
36256             this.split.el.setWidth(box.width);
36257         }
36258         if(this.collapsed){
36259             this.updateBody(box.width, null);
36260         }
36261         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36262     }
36263 });
36264
36265
36266
36267
36268
36269 Roo.bootstrap.layout.South = function(config){
36270     config.region = 'south';
36271     config.cursor = 's-resize';
36272     Roo.bootstrap.layout.Split.call(this, config);
36273     if(this.split){
36274         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36275         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36276         this.split.el.addClass("roo-layout-split-v");
36277     }
36278     var size = config.initialSize || config.height;
36279     if(typeof size != "undefined"){
36280         this.el.setHeight(size);
36281     }
36282 };
36283
36284 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36285     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36286     getBox : function(){
36287         if(this.collapsed){
36288             return this.collapsedEl.getBox();
36289         }
36290         var box = this.el.getBox();
36291         if(this.split){
36292             var sh = this.split.el.getHeight();
36293             box.height += sh;
36294             box.y -= sh;
36295         }
36296         return box;
36297     },
36298     
36299     updateBox : function(box){
36300         if(this.split && !this.collapsed){
36301             var sh = this.split.el.getHeight();
36302             box.height -= sh;
36303             box.y += sh;
36304             this.split.el.setLeft(box.x);
36305             this.split.el.setTop(box.y-sh);
36306             this.split.el.setWidth(box.width);
36307         }
36308         if(this.collapsed){
36309             this.updateBody(box.width, null);
36310         }
36311         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36312     }
36313 });
36314
36315 Roo.bootstrap.layout.East = function(config){
36316     config.region = "east";
36317     config.cursor = "e-resize";
36318     Roo.bootstrap.layout.Split.call(this, config);
36319     if(this.split){
36320         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36321         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36322         this.split.el.addClass("roo-layout-split-h");
36323     }
36324     var size = config.initialSize || config.width;
36325     if(typeof size != "undefined"){
36326         this.el.setWidth(size);
36327     }
36328 };
36329 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36330     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36331     getBox : function(){
36332         if(this.collapsed){
36333             return this.collapsedEl.getBox();
36334         }
36335         var box = this.el.getBox();
36336         if(this.split){
36337             var sw = this.split.el.getWidth();
36338             box.width += sw;
36339             box.x -= sw;
36340         }
36341         return box;
36342     },
36343
36344     updateBox : function(box){
36345         if(this.split && !this.collapsed){
36346             var sw = this.split.el.getWidth();
36347             box.width -= sw;
36348             this.split.el.setLeft(box.x);
36349             this.split.el.setTop(box.y);
36350             this.split.el.setHeight(box.height);
36351             box.x += sw;
36352         }
36353         if(this.collapsed){
36354             this.updateBody(null, box.height);
36355         }
36356         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36357     }
36358 });
36359
36360 Roo.bootstrap.layout.West = function(config){
36361     config.region = "west";
36362     config.cursor = "w-resize";
36363     
36364     Roo.bootstrap.layout.Split.call(this, config);
36365     if(this.split){
36366         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36367         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36368         this.split.el.addClass("roo-layout-split-h");
36369     }
36370     
36371 };
36372 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36373     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36374     
36375     onRender: function(ctr, pos)
36376     {
36377         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36378         var size = this.config.initialSize || this.config.width;
36379         if(typeof size != "undefined"){
36380             this.el.setWidth(size);
36381         }
36382     },
36383     
36384     getBox : function(){
36385         if(this.collapsed){
36386             return this.collapsedEl.getBox();
36387         }
36388         var box = this.el.getBox();
36389         if(this.split){
36390             box.width += this.split.el.getWidth();
36391         }
36392         return box;
36393     },
36394     
36395     updateBox : function(box){
36396         if(this.split && !this.collapsed){
36397             var sw = this.split.el.getWidth();
36398             box.width -= sw;
36399             this.split.el.setLeft(box.x+box.width);
36400             this.split.el.setTop(box.y);
36401             this.split.el.setHeight(box.height);
36402         }
36403         if(this.collapsed){
36404             this.updateBody(null, box.height);
36405         }
36406         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36407     }
36408 });
36409 Roo.namespace("Roo.bootstrap.panel");/*
36410  * Based on:
36411  * Ext JS Library 1.1.1
36412  * Copyright(c) 2006-2007, Ext JS, LLC.
36413  *
36414  * Originally Released Under LGPL - original licence link has changed is not relivant.
36415  *
36416  * Fork - LGPL
36417  * <script type="text/javascript">
36418  */
36419 /**
36420  * @class Roo.ContentPanel
36421  * @extends Roo.util.Observable
36422  * A basic ContentPanel element.
36423  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36424  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36425  * @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
36426  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36427  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36428  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36429  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36430  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36431  * @cfg {String} title          The title for this panel
36432  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36433  * @cfg {String} url            Calls {@link #setUrl} with this value
36434  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36435  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36436  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36437  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36438  * @cfg {Boolean} badges render the badges
36439
36440  * @constructor
36441  * Create a new ContentPanel.
36442  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36443  * @param {String/Object} config A string to set only the title or a config object
36444  * @param {String} content (optional) Set the HTML content for this panel
36445  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36446  */
36447 Roo.bootstrap.panel.Content = function( config){
36448     
36449     this.tpl = config.tpl || false;
36450     
36451     var el = config.el;
36452     var content = config.content;
36453
36454     if(config.autoCreate){ // xtype is available if this is called from factory
36455         el = Roo.id();
36456     }
36457     this.el = Roo.get(el);
36458     if(!this.el && config && config.autoCreate){
36459         if(typeof config.autoCreate == "object"){
36460             if(!config.autoCreate.id){
36461                 config.autoCreate.id = config.id||el;
36462             }
36463             this.el = Roo.DomHelper.append(document.body,
36464                         config.autoCreate, true);
36465         }else{
36466             var elcfg =  {   tag: "div",
36467                             cls: "roo-layout-inactive-content",
36468                             id: config.id||el
36469                             };
36470             if (config.html) {
36471                 elcfg.html = config.html;
36472                 
36473             }
36474                         
36475             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36476         }
36477     } 
36478     this.closable = false;
36479     this.loaded = false;
36480     this.active = false;
36481    
36482       
36483     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36484         
36485         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36486         
36487         this.wrapEl = this.el; //this.el.wrap();
36488         var ti = [];
36489         if (config.toolbar.items) {
36490             ti = config.toolbar.items ;
36491             delete config.toolbar.items ;
36492         }
36493         
36494         var nitems = [];
36495         this.toolbar.render(this.wrapEl, 'before');
36496         for(var i =0;i < ti.length;i++) {
36497           //  Roo.log(['add child', items[i]]);
36498             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36499         }
36500         this.toolbar.items = nitems;
36501         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36502         delete config.toolbar;
36503         
36504     }
36505     /*
36506     // xtype created footer. - not sure if will work as we normally have to render first..
36507     if (this.footer && !this.footer.el && this.footer.xtype) {
36508         if (!this.wrapEl) {
36509             this.wrapEl = this.el.wrap();
36510         }
36511     
36512         this.footer.container = this.wrapEl.createChild();
36513          
36514         this.footer = Roo.factory(this.footer, Roo);
36515         
36516     }
36517     */
36518     
36519      if(typeof config == "string"){
36520         this.title = config;
36521     }else{
36522         Roo.apply(this, config);
36523     }
36524     
36525     if(this.resizeEl){
36526         this.resizeEl = Roo.get(this.resizeEl, true);
36527     }else{
36528         this.resizeEl = this.el;
36529     }
36530     // handle view.xtype
36531     
36532  
36533     
36534     
36535     this.addEvents({
36536         /**
36537          * @event activate
36538          * Fires when this panel is activated. 
36539          * @param {Roo.ContentPanel} this
36540          */
36541         "activate" : true,
36542         /**
36543          * @event deactivate
36544          * Fires when this panel is activated. 
36545          * @param {Roo.ContentPanel} this
36546          */
36547         "deactivate" : true,
36548
36549         /**
36550          * @event resize
36551          * Fires when this panel is resized if fitToFrame is true.
36552          * @param {Roo.ContentPanel} this
36553          * @param {Number} width The width after any component adjustments
36554          * @param {Number} height The height after any component adjustments
36555          */
36556         "resize" : true,
36557         
36558          /**
36559          * @event render
36560          * Fires when this tab is created
36561          * @param {Roo.ContentPanel} this
36562          */
36563         "render" : true
36564         
36565         
36566         
36567     });
36568     
36569
36570     
36571     
36572     if(this.autoScroll){
36573         this.resizeEl.setStyle("overflow", "auto");
36574     } else {
36575         // fix randome scrolling
36576         //this.el.on('scroll', function() {
36577         //    Roo.log('fix random scolling');
36578         //    this.scrollTo('top',0); 
36579         //});
36580     }
36581     content = content || this.content;
36582     if(content){
36583         this.setContent(content);
36584     }
36585     if(config && config.url){
36586         this.setUrl(this.url, this.params, this.loadOnce);
36587     }
36588     
36589     
36590     
36591     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36592     
36593     if (this.view && typeof(this.view.xtype) != 'undefined') {
36594         this.view.el = this.el.appendChild(document.createElement("div"));
36595         this.view = Roo.factory(this.view); 
36596         this.view.render  &&  this.view.render(false, '');  
36597     }
36598     
36599     
36600     this.fireEvent('render', this);
36601 };
36602
36603 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36604     
36605     tabTip : '',
36606     
36607     setRegion : function(region){
36608         this.region = region;
36609         this.setActiveClass(region && !this.background);
36610     },
36611     
36612     
36613     setActiveClass: function(state)
36614     {
36615         if(state){
36616            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36617            this.el.setStyle('position','relative');
36618         }else{
36619            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36620            this.el.setStyle('position', 'absolute');
36621         } 
36622     },
36623     
36624     /**
36625      * Returns the toolbar for this Panel if one was configured. 
36626      * @return {Roo.Toolbar} 
36627      */
36628     getToolbar : function(){
36629         return this.toolbar;
36630     },
36631     
36632     setActiveState : function(active)
36633     {
36634         this.active = active;
36635         this.setActiveClass(active);
36636         if(!active){
36637             if(this.fireEvent("deactivate", this) === false){
36638                 return false;
36639             }
36640             return true;
36641         }
36642         this.fireEvent("activate", this);
36643         return true;
36644     },
36645     /**
36646      * Updates this panel's element
36647      * @param {String} content The new content
36648      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36649     */
36650     setContent : function(content, loadScripts){
36651         this.el.update(content, loadScripts);
36652     },
36653
36654     ignoreResize : function(w, h){
36655         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36656             return true;
36657         }else{
36658             this.lastSize = {width: w, height: h};
36659             return false;
36660         }
36661     },
36662     /**
36663      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36664      * @return {Roo.UpdateManager} The UpdateManager
36665      */
36666     getUpdateManager : function(){
36667         return this.el.getUpdateManager();
36668     },
36669      /**
36670      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36671      * @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:
36672 <pre><code>
36673 panel.load({
36674     url: "your-url.php",
36675     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36676     callback: yourFunction,
36677     scope: yourObject, //(optional scope)
36678     discardUrl: false,
36679     nocache: false,
36680     text: "Loading...",
36681     timeout: 30,
36682     scripts: false
36683 });
36684 </code></pre>
36685      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36686      * 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.
36687      * @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}
36688      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36689      * @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.
36690      * @return {Roo.ContentPanel} this
36691      */
36692     load : function(){
36693         var um = this.el.getUpdateManager();
36694         um.update.apply(um, arguments);
36695         return this;
36696     },
36697
36698
36699     /**
36700      * 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.
36701      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36702      * @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)
36703      * @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)
36704      * @return {Roo.UpdateManager} The UpdateManager
36705      */
36706     setUrl : function(url, params, loadOnce){
36707         if(this.refreshDelegate){
36708             this.removeListener("activate", this.refreshDelegate);
36709         }
36710         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36711         this.on("activate", this.refreshDelegate);
36712         return this.el.getUpdateManager();
36713     },
36714     
36715     _handleRefresh : function(url, params, loadOnce){
36716         if(!loadOnce || !this.loaded){
36717             var updater = this.el.getUpdateManager();
36718             updater.update(url, params, this._setLoaded.createDelegate(this));
36719         }
36720     },
36721     
36722     _setLoaded : function(){
36723         this.loaded = true;
36724     }, 
36725     
36726     /**
36727      * Returns this panel's id
36728      * @return {String} 
36729      */
36730     getId : function(){
36731         return this.el.id;
36732     },
36733     
36734     /** 
36735      * Returns this panel's element - used by regiosn to add.
36736      * @return {Roo.Element} 
36737      */
36738     getEl : function(){
36739         return this.wrapEl || this.el;
36740     },
36741     
36742    
36743     
36744     adjustForComponents : function(width, height)
36745     {
36746         //Roo.log('adjustForComponents ');
36747         if(this.resizeEl != this.el){
36748             width -= this.el.getFrameWidth('lr');
36749             height -= this.el.getFrameWidth('tb');
36750         }
36751         if(this.toolbar){
36752             var te = this.toolbar.getEl();
36753             te.setWidth(width);
36754             height -= te.getHeight();
36755         }
36756         if(this.footer){
36757             var te = this.footer.getEl();
36758             te.setWidth(width);
36759             height -= te.getHeight();
36760         }
36761         
36762         
36763         if(this.adjustments){
36764             width += this.adjustments[0];
36765             height += this.adjustments[1];
36766         }
36767         return {"width": width, "height": height};
36768     },
36769     
36770     setSize : function(width, height){
36771         if(this.fitToFrame && !this.ignoreResize(width, height)){
36772             if(this.fitContainer && this.resizeEl != this.el){
36773                 this.el.setSize(width, height);
36774             }
36775             var size = this.adjustForComponents(width, height);
36776             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36777             this.fireEvent('resize', this, size.width, size.height);
36778         }
36779     },
36780     
36781     /**
36782      * Returns this panel's title
36783      * @return {String} 
36784      */
36785     getTitle : function(){
36786         
36787         if (typeof(this.title) != 'object') {
36788             return this.title;
36789         }
36790         
36791         var t = '';
36792         for (var k in this.title) {
36793             if (!this.title.hasOwnProperty(k)) {
36794                 continue;
36795             }
36796             
36797             if (k.indexOf('-') >= 0) {
36798                 var s = k.split('-');
36799                 for (var i = 0; i<s.length; i++) {
36800                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36801                 }
36802             } else {
36803                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36804             }
36805         }
36806         return t;
36807     },
36808     
36809     /**
36810      * Set this panel's title
36811      * @param {String} title
36812      */
36813     setTitle : function(title){
36814         this.title = title;
36815         if(this.region){
36816             this.region.updatePanelTitle(this, title);
36817         }
36818     },
36819     
36820     /**
36821      * Returns true is this panel was configured to be closable
36822      * @return {Boolean} 
36823      */
36824     isClosable : function(){
36825         return this.closable;
36826     },
36827     
36828     beforeSlide : function(){
36829         this.el.clip();
36830         this.resizeEl.clip();
36831     },
36832     
36833     afterSlide : function(){
36834         this.el.unclip();
36835         this.resizeEl.unclip();
36836     },
36837     
36838     /**
36839      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36840      *   Will fail silently if the {@link #setUrl} method has not been called.
36841      *   This does not activate the panel, just updates its content.
36842      */
36843     refresh : function(){
36844         if(this.refreshDelegate){
36845            this.loaded = false;
36846            this.refreshDelegate();
36847         }
36848     },
36849     
36850     /**
36851      * Destroys this panel
36852      */
36853     destroy : function(){
36854         this.el.removeAllListeners();
36855         var tempEl = document.createElement("span");
36856         tempEl.appendChild(this.el.dom);
36857         tempEl.innerHTML = "";
36858         this.el.remove();
36859         this.el = null;
36860     },
36861     
36862     /**
36863      * form - if the content panel contains a form - this is a reference to it.
36864      * @type {Roo.form.Form}
36865      */
36866     form : false,
36867     /**
36868      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36869      *    This contains a reference to it.
36870      * @type {Roo.View}
36871      */
36872     view : false,
36873     
36874       /**
36875      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36876      * <pre><code>
36877
36878 layout.addxtype({
36879        xtype : 'Form',
36880        items: [ .... ]
36881    }
36882 );
36883
36884 </code></pre>
36885      * @param {Object} cfg Xtype definition of item to add.
36886      */
36887     
36888     
36889     getChildContainer: function () {
36890         return this.getEl();
36891     }
36892     
36893     
36894     /*
36895         var  ret = new Roo.factory(cfg);
36896         return ret;
36897         
36898         
36899         // add form..
36900         if (cfg.xtype.match(/^Form$/)) {
36901             
36902             var el;
36903             //if (this.footer) {
36904             //    el = this.footer.container.insertSibling(false, 'before');
36905             //} else {
36906                 el = this.el.createChild();
36907             //}
36908
36909             this.form = new  Roo.form.Form(cfg);
36910             
36911             
36912             if ( this.form.allItems.length) {
36913                 this.form.render(el.dom);
36914             }
36915             return this.form;
36916         }
36917         // should only have one of theses..
36918         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36919             // views.. should not be just added - used named prop 'view''
36920             
36921             cfg.el = this.el.appendChild(document.createElement("div"));
36922             // factory?
36923             
36924             var ret = new Roo.factory(cfg);
36925              
36926              ret.render && ret.render(false, ''); // render blank..
36927             this.view = ret;
36928             return ret;
36929         }
36930         return false;
36931     }
36932     \*/
36933 });
36934  
36935 /**
36936  * @class Roo.bootstrap.panel.Grid
36937  * @extends Roo.bootstrap.panel.Content
36938  * @constructor
36939  * Create a new GridPanel.
36940  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36941  * @param {Object} config A the config object
36942   
36943  */
36944
36945
36946
36947 Roo.bootstrap.panel.Grid = function(config)
36948 {
36949     
36950       
36951     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36952         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36953
36954     config.el = this.wrapper;
36955     //this.el = this.wrapper;
36956     
36957       if (config.container) {
36958         // ctor'ed from a Border/panel.grid
36959         
36960         
36961         this.wrapper.setStyle("overflow", "hidden");
36962         this.wrapper.addClass('roo-grid-container');
36963
36964     }
36965     
36966     
36967     if(config.toolbar){
36968         var tool_el = this.wrapper.createChild();    
36969         this.toolbar = Roo.factory(config.toolbar);
36970         var ti = [];
36971         if (config.toolbar.items) {
36972             ti = config.toolbar.items ;
36973             delete config.toolbar.items ;
36974         }
36975         
36976         var nitems = [];
36977         this.toolbar.render(tool_el);
36978         for(var i =0;i < ti.length;i++) {
36979           //  Roo.log(['add child', items[i]]);
36980             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36981         }
36982         this.toolbar.items = nitems;
36983         
36984         delete config.toolbar;
36985     }
36986     
36987     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36988     config.grid.scrollBody = true;;
36989     config.grid.monitorWindowResize = false; // turn off autosizing
36990     config.grid.autoHeight = false;
36991     config.grid.autoWidth = false;
36992     
36993     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36994     
36995     if (config.background) {
36996         // render grid on panel activation (if panel background)
36997         this.on('activate', function(gp) {
36998             if (!gp.grid.rendered) {
36999                 gp.grid.render(this.wrapper);
37000                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37001             }
37002         });
37003             
37004     } else {
37005         this.grid.render(this.wrapper);
37006         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37007
37008     }
37009     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37010     // ??? needed ??? config.el = this.wrapper;
37011     
37012     
37013     
37014   
37015     // xtype created footer. - not sure if will work as we normally have to render first..
37016     if (this.footer && !this.footer.el && this.footer.xtype) {
37017         
37018         var ctr = this.grid.getView().getFooterPanel(true);
37019         this.footer.dataSource = this.grid.dataSource;
37020         this.footer = Roo.factory(this.footer, Roo);
37021         this.footer.render(ctr);
37022         
37023     }
37024     
37025     
37026     
37027     
37028      
37029 };
37030
37031 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37032     getId : function(){
37033         return this.grid.id;
37034     },
37035     
37036     /**
37037      * Returns the grid for this panel
37038      * @return {Roo.bootstrap.Table} 
37039      */
37040     getGrid : function(){
37041         return this.grid;    
37042     },
37043     
37044     setSize : function(width, height){
37045         if(!this.ignoreResize(width, height)){
37046             var grid = this.grid;
37047             var size = this.adjustForComponents(width, height);
37048             var gridel = grid.getGridEl();
37049             gridel.setSize(size.width, size.height);
37050             /*
37051             var thd = grid.getGridEl().select('thead',true).first();
37052             var tbd = grid.getGridEl().select('tbody', true).first();
37053             if (tbd) {
37054                 tbd.setSize(width, height - thd.getHeight());
37055             }
37056             */
37057             grid.autoSize();
37058         }
37059     },
37060      
37061     
37062     
37063     beforeSlide : function(){
37064         this.grid.getView().scroller.clip();
37065     },
37066     
37067     afterSlide : function(){
37068         this.grid.getView().scroller.unclip();
37069     },
37070     
37071     destroy : function(){
37072         this.grid.destroy();
37073         delete this.grid;
37074         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37075     }
37076 });
37077
37078 /**
37079  * @class Roo.bootstrap.panel.Nest
37080  * @extends Roo.bootstrap.panel.Content
37081  * @constructor
37082  * Create a new Panel, that can contain a layout.Border.
37083  * 
37084  * 
37085  * @param {Roo.BorderLayout} layout The layout for this panel
37086  * @param {String/Object} config A string to set only the title or a config object
37087  */
37088 Roo.bootstrap.panel.Nest = function(config)
37089 {
37090     // construct with only one argument..
37091     /* FIXME - implement nicer consturctors
37092     if (layout.layout) {
37093         config = layout;
37094         layout = config.layout;
37095         delete config.layout;
37096     }
37097     if (layout.xtype && !layout.getEl) {
37098         // then layout needs constructing..
37099         layout = Roo.factory(layout, Roo);
37100     }
37101     */
37102     
37103     config.el =  config.layout.getEl();
37104     
37105     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37106     
37107     config.layout.monitorWindowResize = false; // turn off autosizing
37108     this.layout = config.layout;
37109     this.layout.getEl().addClass("roo-layout-nested-layout");
37110     
37111     
37112     
37113     
37114 };
37115
37116 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37117
37118     setSize : function(width, height){
37119         if(!this.ignoreResize(width, height)){
37120             var size = this.adjustForComponents(width, height);
37121             var el = this.layout.getEl();
37122             if (size.height < 1) {
37123                 el.setWidth(size.width);   
37124             } else {
37125                 el.setSize(size.width, size.height);
37126             }
37127             var touch = el.dom.offsetWidth;
37128             this.layout.layout();
37129             // ie requires a double layout on the first pass
37130             if(Roo.isIE && !this.initialized){
37131                 this.initialized = true;
37132                 this.layout.layout();
37133             }
37134         }
37135     },
37136     
37137     // activate all subpanels if not currently active..
37138     
37139     setActiveState : function(active){
37140         this.active = active;
37141         this.setActiveClass(active);
37142         
37143         if(!active){
37144             this.fireEvent("deactivate", this);
37145             return;
37146         }
37147         
37148         this.fireEvent("activate", this);
37149         // not sure if this should happen before or after..
37150         if (!this.layout) {
37151             return; // should not happen..
37152         }
37153         var reg = false;
37154         for (var r in this.layout.regions) {
37155             reg = this.layout.getRegion(r);
37156             if (reg.getActivePanel()) {
37157                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37158                 reg.setActivePanel(reg.getActivePanel());
37159                 continue;
37160             }
37161             if (!reg.panels.length) {
37162                 continue;
37163             }
37164             reg.showPanel(reg.getPanel(0));
37165         }
37166         
37167         
37168         
37169         
37170     },
37171     
37172     /**
37173      * Returns the nested BorderLayout for this panel
37174      * @return {Roo.BorderLayout} 
37175      */
37176     getLayout : function(){
37177         return this.layout;
37178     },
37179     
37180      /**
37181      * Adds a xtype elements to the layout of the nested panel
37182      * <pre><code>
37183
37184 panel.addxtype({
37185        xtype : 'ContentPanel',
37186        region: 'west',
37187        items: [ .... ]
37188    }
37189 );
37190
37191 panel.addxtype({
37192         xtype : 'NestedLayoutPanel',
37193         region: 'west',
37194         layout: {
37195            center: { },
37196            west: { }   
37197         },
37198         items : [ ... list of content panels or nested layout panels.. ]
37199    }
37200 );
37201 </code></pre>
37202      * @param {Object} cfg Xtype definition of item to add.
37203      */
37204     addxtype : function(cfg) {
37205         return this.layout.addxtype(cfg);
37206     
37207     }
37208 });        /*
37209  * Based on:
37210  * Ext JS Library 1.1.1
37211  * Copyright(c) 2006-2007, Ext JS, LLC.
37212  *
37213  * Originally Released Under LGPL - original licence link has changed is not relivant.
37214  *
37215  * Fork - LGPL
37216  * <script type="text/javascript">
37217  */
37218 /**
37219  * @class Roo.TabPanel
37220  * @extends Roo.util.Observable
37221  * A lightweight tab container.
37222  * <br><br>
37223  * Usage:
37224  * <pre><code>
37225 // basic tabs 1, built from existing content
37226 var tabs = new Roo.TabPanel("tabs1");
37227 tabs.addTab("script", "View Script");
37228 tabs.addTab("markup", "View Markup");
37229 tabs.activate("script");
37230
37231 // more advanced tabs, built from javascript
37232 var jtabs = new Roo.TabPanel("jtabs");
37233 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37234
37235 // set up the UpdateManager
37236 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37237 var updater = tab2.getUpdateManager();
37238 updater.setDefaultUrl("ajax1.htm");
37239 tab2.on('activate', updater.refresh, updater, true);
37240
37241 // Use setUrl for Ajax loading
37242 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37243 tab3.setUrl("ajax2.htm", null, true);
37244
37245 // Disabled tab
37246 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37247 tab4.disable();
37248
37249 jtabs.activate("jtabs-1");
37250  * </code></pre>
37251  * @constructor
37252  * Create a new TabPanel.
37253  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37254  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37255  */
37256 Roo.bootstrap.panel.Tabs = function(config){
37257     /**
37258     * The container element for this TabPanel.
37259     * @type Roo.Element
37260     */
37261     this.el = Roo.get(config.el);
37262     delete config.el;
37263     if(config){
37264         if(typeof config == "boolean"){
37265             this.tabPosition = config ? "bottom" : "top";
37266         }else{
37267             Roo.apply(this, config);
37268         }
37269     }
37270     
37271     if(this.tabPosition == "bottom"){
37272         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37273         this.el.addClass("roo-tabs-bottom");
37274     }
37275     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37276     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37277     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37278     if(Roo.isIE){
37279         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37280     }
37281     if(this.tabPosition != "bottom"){
37282         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37283          * @type Roo.Element
37284          */
37285         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37286         this.el.addClass("roo-tabs-top");
37287     }
37288     this.items = [];
37289
37290     this.bodyEl.setStyle("position", "relative");
37291
37292     this.active = null;
37293     this.activateDelegate = this.activate.createDelegate(this);
37294
37295     this.addEvents({
37296         /**
37297          * @event tabchange
37298          * Fires when the active tab changes
37299          * @param {Roo.TabPanel} this
37300          * @param {Roo.TabPanelItem} activePanel The new active tab
37301          */
37302         "tabchange": true,
37303         /**
37304          * @event beforetabchange
37305          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37306          * @param {Roo.TabPanel} this
37307          * @param {Object} e Set cancel to true on this object to cancel the tab change
37308          * @param {Roo.TabPanelItem} tab The tab being changed to
37309          */
37310         "beforetabchange" : true
37311     });
37312
37313     Roo.EventManager.onWindowResize(this.onResize, this);
37314     this.cpad = this.el.getPadding("lr");
37315     this.hiddenCount = 0;
37316
37317
37318     // toolbar on the tabbar support...
37319     if (this.toolbar) {
37320         alert("no toolbar support yet");
37321         this.toolbar  = false;
37322         /*
37323         var tcfg = this.toolbar;
37324         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37325         this.toolbar = new Roo.Toolbar(tcfg);
37326         if (Roo.isSafari) {
37327             var tbl = tcfg.container.child('table', true);
37328             tbl.setAttribute('width', '100%');
37329         }
37330         */
37331         
37332     }
37333    
37334
37335
37336     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37337 };
37338
37339 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37340     /*
37341      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37342      */
37343     tabPosition : "top",
37344     /*
37345      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37346      */
37347     currentTabWidth : 0,
37348     /*
37349      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37350      */
37351     minTabWidth : 40,
37352     /*
37353      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37354      */
37355     maxTabWidth : 250,
37356     /*
37357      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37358      */
37359     preferredTabWidth : 175,
37360     /*
37361      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37362      */
37363     resizeTabs : false,
37364     /*
37365      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37366      */
37367     monitorResize : true,
37368     /*
37369      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37370      */
37371     toolbar : false,
37372
37373     /**
37374      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37375      * @param {String} id The id of the div to use <b>or create</b>
37376      * @param {String} text The text for the tab
37377      * @param {String} content (optional) Content to put in the TabPanelItem body
37378      * @param {Boolean} closable (optional) True to create a close icon on the tab
37379      * @return {Roo.TabPanelItem} The created TabPanelItem
37380      */
37381     addTab : function(id, text, content, closable, tpl)
37382     {
37383         var item = new Roo.bootstrap.panel.TabItem({
37384             panel: this,
37385             id : id,
37386             text : text,
37387             closable : closable,
37388             tpl : tpl
37389         });
37390         this.addTabItem(item);
37391         if(content){
37392             item.setContent(content);
37393         }
37394         return item;
37395     },
37396
37397     /**
37398      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37399      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37400      * @return {Roo.TabPanelItem}
37401      */
37402     getTab : function(id){
37403         return this.items[id];
37404     },
37405
37406     /**
37407      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37408      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37409      */
37410     hideTab : function(id){
37411         var t = this.items[id];
37412         if(!t.isHidden()){
37413            t.setHidden(true);
37414            this.hiddenCount++;
37415            this.autoSizeTabs();
37416         }
37417     },
37418
37419     /**
37420      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37421      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37422      */
37423     unhideTab : function(id){
37424         var t = this.items[id];
37425         if(t.isHidden()){
37426            t.setHidden(false);
37427            this.hiddenCount--;
37428            this.autoSizeTabs();
37429         }
37430     },
37431
37432     /**
37433      * Adds an existing {@link Roo.TabPanelItem}.
37434      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37435      */
37436     addTabItem : function(item){
37437         this.items[item.id] = item;
37438         this.items.push(item);
37439       //  if(this.resizeTabs){
37440     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37441   //         this.autoSizeTabs();
37442 //        }else{
37443 //            item.autoSize();
37444        // }
37445     },
37446
37447     /**
37448      * Removes a {@link Roo.TabPanelItem}.
37449      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37450      */
37451     removeTab : function(id){
37452         var items = this.items;
37453         var tab = items[id];
37454         if(!tab) { return; }
37455         var index = items.indexOf(tab);
37456         if(this.active == tab && items.length > 1){
37457             var newTab = this.getNextAvailable(index);
37458             if(newTab) {
37459                 newTab.activate();
37460             }
37461         }
37462         this.stripEl.dom.removeChild(tab.pnode.dom);
37463         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37464             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37465         }
37466         items.splice(index, 1);
37467         delete this.items[tab.id];
37468         tab.fireEvent("close", tab);
37469         tab.purgeListeners();
37470         this.autoSizeTabs();
37471     },
37472
37473     getNextAvailable : function(start){
37474         var items = this.items;
37475         var index = start;
37476         // look for a next tab that will slide over to
37477         // replace the one being removed
37478         while(index < items.length){
37479             var item = items[++index];
37480             if(item && !item.isHidden()){
37481                 return item;
37482             }
37483         }
37484         // if one isn't found select the previous tab (on the left)
37485         index = start;
37486         while(index >= 0){
37487             var item = items[--index];
37488             if(item && !item.isHidden()){
37489                 return item;
37490             }
37491         }
37492         return null;
37493     },
37494
37495     /**
37496      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37497      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37498      */
37499     disableTab : function(id){
37500         var tab = this.items[id];
37501         if(tab && this.active != tab){
37502             tab.disable();
37503         }
37504     },
37505
37506     /**
37507      * Enables a {@link Roo.TabPanelItem} that is disabled.
37508      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37509      */
37510     enableTab : function(id){
37511         var tab = this.items[id];
37512         tab.enable();
37513     },
37514
37515     /**
37516      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37517      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37518      * @return {Roo.TabPanelItem} The TabPanelItem.
37519      */
37520     activate : function(id){
37521         var tab = this.items[id];
37522         if(!tab){
37523             return null;
37524         }
37525         if(tab == this.active || tab.disabled){
37526             return tab;
37527         }
37528         var e = {};
37529         this.fireEvent("beforetabchange", this, e, tab);
37530         if(e.cancel !== true && !tab.disabled){
37531             if(this.active){
37532                 this.active.hide();
37533             }
37534             this.active = this.items[id];
37535             this.active.show();
37536             this.fireEvent("tabchange", this, this.active);
37537         }
37538         return tab;
37539     },
37540
37541     /**
37542      * Gets the active {@link Roo.TabPanelItem}.
37543      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37544      */
37545     getActiveTab : function(){
37546         return this.active;
37547     },
37548
37549     /**
37550      * Updates the tab body element to fit the height of the container element
37551      * for overflow scrolling
37552      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37553      */
37554     syncHeight : function(targetHeight){
37555         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37556         var bm = this.bodyEl.getMargins();
37557         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37558         this.bodyEl.setHeight(newHeight);
37559         return newHeight;
37560     },
37561
37562     onResize : function(){
37563         if(this.monitorResize){
37564             this.autoSizeTabs();
37565         }
37566     },
37567
37568     /**
37569      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37570      */
37571     beginUpdate : function(){
37572         this.updating = true;
37573     },
37574
37575     /**
37576      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37577      */
37578     endUpdate : function(){
37579         this.updating = false;
37580         this.autoSizeTabs();
37581     },
37582
37583     /**
37584      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37585      */
37586     autoSizeTabs : function(){
37587         var count = this.items.length;
37588         var vcount = count - this.hiddenCount;
37589         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37590             return;
37591         }
37592         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37593         var availWidth = Math.floor(w / vcount);
37594         var b = this.stripBody;
37595         if(b.getWidth() > w){
37596             var tabs = this.items;
37597             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37598             if(availWidth < this.minTabWidth){
37599                 /*if(!this.sleft){    // incomplete scrolling code
37600                     this.createScrollButtons();
37601                 }
37602                 this.showScroll();
37603                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37604             }
37605         }else{
37606             if(this.currentTabWidth < this.preferredTabWidth){
37607                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37608             }
37609         }
37610     },
37611
37612     /**
37613      * Returns the number of tabs in this TabPanel.
37614      * @return {Number}
37615      */
37616      getCount : function(){
37617          return this.items.length;
37618      },
37619
37620     /**
37621      * Resizes all the tabs to the passed width
37622      * @param {Number} The new width
37623      */
37624     setTabWidth : function(width){
37625         this.currentTabWidth = width;
37626         for(var i = 0, len = this.items.length; i < len; i++) {
37627                 if(!this.items[i].isHidden()) {
37628                 this.items[i].setWidth(width);
37629             }
37630         }
37631     },
37632
37633     /**
37634      * Destroys this TabPanel
37635      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37636      */
37637     destroy : function(removeEl){
37638         Roo.EventManager.removeResizeListener(this.onResize, this);
37639         for(var i = 0, len = this.items.length; i < len; i++){
37640             this.items[i].purgeListeners();
37641         }
37642         if(removeEl === true){
37643             this.el.update("");
37644             this.el.remove();
37645         }
37646     },
37647     
37648     createStrip : function(container)
37649     {
37650         var strip = document.createElement("nav");
37651         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37652         container.appendChild(strip);
37653         return strip;
37654     },
37655     
37656     createStripList : function(strip)
37657     {
37658         // div wrapper for retard IE
37659         // returns the "tr" element.
37660         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37661         //'<div class="x-tabs-strip-wrap">'+
37662           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37663           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37664         return strip.firstChild; //.firstChild.firstChild.firstChild;
37665     },
37666     createBody : function(container)
37667     {
37668         var body = document.createElement("div");
37669         Roo.id(body, "tab-body");
37670         //Roo.fly(body).addClass("x-tabs-body");
37671         Roo.fly(body).addClass("tab-content");
37672         container.appendChild(body);
37673         return body;
37674     },
37675     createItemBody :function(bodyEl, id){
37676         var body = Roo.getDom(id);
37677         if(!body){
37678             body = document.createElement("div");
37679             body.id = id;
37680         }
37681         //Roo.fly(body).addClass("x-tabs-item-body");
37682         Roo.fly(body).addClass("tab-pane");
37683          bodyEl.insertBefore(body, bodyEl.firstChild);
37684         return body;
37685     },
37686     /** @private */
37687     createStripElements :  function(stripEl, text, closable, tpl)
37688     {
37689         var td = document.createElement("li"); // was td..
37690         
37691         
37692         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37693         
37694         
37695         stripEl.appendChild(td);
37696         /*if(closable){
37697             td.className = "x-tabs-closable";
37698             if(!this.closeTpl){
37699                 this.closeTpl = new Roo.Template(
37700                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37701                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37702                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37703                 );
37704             }
37705             var el = this.closeTpl.overwrite(td, {"text": text});
37706             var close = el.getElementsByTagName("div")[0];
37707             var inner = el.getElementsByTagName("em")[0];
37708             return {"el": el, "close": close, "inner": inner};
37709         } else {
37710         */
37711         // not sure what this is..
37712 //            if(!this.tabTpl){
37713                 //this.tabTpl = new Roo.Template(
37714                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37715                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37716                 //);
37717 //                this.tabTpl = new Roo.Template(
37718 //                   '<a href="#">' +
37719 //                   '<span unselectable="on"' +
37720 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37721 //                            ' >{text}</span></a>'
37722 //                );
37723 //                
37724 //            }
37725
37726
37727             var template = tpl || this.tabTpl || false;
37728             
37729             if(!template){
37730                 
37731                 template = new Roo.Template(
37732                    '<a href="#">' +
37733                    '<span unselectable="on"' +
37734                             (this.disableTooltips ? '' : ' title="{text}"') +
37735                             ' >{text}</span></a>'
37736                 );
37737             }
37738             
37739             switch (typeof(template)) {
37740                 case 'object' :
37741                     break;
37742                 case 'string' :
37743                     template = new Roo.Template(template);
37744                     break;
37745                 default :
37746                     break;
37747             }
37748             
37749             var el = template.overwrite(td, {"text": text});
37750             
37751             var inner = el.getElementsByTagName("span")[0];
37752             
37753             return {"el": el, "inner": inner};
37754             
37755     }
37756         
37757     
37758 });
37759
37760 /**
37761  * @class Roo.TabPanelItem
37762  * @extends Roo.util.Observable
37763  * Represents an individual item (tab plus body) in a TabPanel.
37764  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37765  * @param {String} id The id of this TabPanelItem
37766  * @param {String} text The text for the tab of this TabPanelItem
37767  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37768  */
37769 Roo.bootstrap.panel.TabItem = function(config){
37770     /**
37771      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37772      * @type Roo.TabPanel
37773      */
37774     this.tabPanel = config.panel;
37775     /**
37776      * The id for this TabPanelItem
37777      * @type String
37778      */
37779     this.id = config.id;
37780     /** @private */
37781     this.disabled = false;
37782     /** @private */
37783     this.text = config.text;
37784     /** @private */
37785     this.loaded = false;
37786     this.closable = config.closable;
37787
37788     /**
37789      * The body element for this TabPanelItem.
37790      * @type Roo.Element
37791      */
37792     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37793     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37794     this.bodyEl.setStyle("display", "block");
37795     this.bodyEl.setStyle("zoom", "1");
37796     //this.hideAction();
37797
37798     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37799     /** @private */
37800     this.el = Roo.get(els.el);
37801     this.inner = Roo.get(els.inner, true);
37802     this.textEl = Roo.get(this.el.dom.firstChild, true);
37803     this.pnode = Roo.get(els.el.parentNode, true);
37804 //    this.el.on("mousedown", this.onTabMouseDown, this);
37805     this.el.on("click", this.onTabClick, this);
37806     /** @private */
37807     if(config.closable){
37808         var c = Roo.get(els.close, true);
37809         c.dom.title = this.closeText;
37810         c.addClassOnOver("close-over");
37811         c.on("click", this.closeClick, this);
37812      }
37813
37814     this.addEvents({
37815          /**
37816          * @event activate
37817          * Fires when this tab becomes the active tab.
37818          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37819          * @param {Roo.TabPanelItem} this
37820          */
37821         "activate": true,
37822         /**
37823          * @event beforeclose
37824          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37825          * @param {Roo.TabPanelItem} this
37826          * @param {Object} e Set cancel to true on this object to cancel the close.
37827          */
37828         "beforeclose": true,
37829         /**
37830          * @event close
37831          * Fires when this tab is closed.
37832          * @param {Roo.TabPanelItem} this
37833          */
37834          "close": true,
37835         /**
37836          * @event deactivate
37837          * Fires when this tab is no longer the active tab.
37838          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37839          * @param {Roo.TabPanelItem} this
37840          */
37841          "deactivate" : true
37842     });
37843     this.hidden = false;
37844
37845     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37846 };
37847
37848 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37849            {
37850     purgeListeners : function(){
37851        Roo.util.Observable.prototype.purgeListeners.call(this);
37852        this.el.removeAllListeners();
37853     },
37854     /**
37855      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37856      */
37857     show : function(){
37858         this.pnode.addClass("active");
37859         this.showAction();
37860         if(Roo.isOpera){
37861             this.tabPanel.stripWrap.repaint();
37862         }
37863         this.fireEvent("activate", this.tabPanel, this);
37864     },
37865
37866     /**
37867      * Returns true if this tab is the active tab.
37868      * @return {Boolean}
37869      */
37870     isActive : function(){
37871         return this.tabPanel.getActiveTab() == this;
37872     },
37873
37874     /**
37875      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37876      */
37877     hide : function(){
37878         this.pnode.removeClass("active");
37879         this.hideAction();
37880         this.fireEvent("deactivate", this.tabPanel, this);
37881     },
37882
37883     hideAction : function(){
37884         this.bodyEl.hide();
37885         this.bodyEl.setStyle("position", "absolute");
37886         this.bodyEl.setLeft("-20000px");
37887         this.bodyEl.setTop("-20000px");
37888     },
37889
37890     showAction : function(){
37891         this.bodyEl.setStyle("position", "relative");
37892         this.bodyEl.setTop("");
37893         this.bodyEl.setLeft("");
37894         this.bodyEl.show();
37895     },
37896
37897     /**
37898      * Set the tooltip for the tab.
37899      * @param {String} tooltip The tab's tooltip
37900      */
37901     setTooltip : function(text){
37902         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37903             this.textEl.dom.qtip = text;
37904             this.textEl.dom.removeAttribute('title');
37905         }else{
37906             this.textEl.dom.title = text;
37907         }
37908     },
37909
37910     onTabClick : function(e){
37911         e.preventDefault();
37912         this.tabPanel.activate(this.id);
37913     },
37914
37915     onTabMouseDown : function(e){
37916         e.preventDefault();
37917         this.tabPanel.activate(this.id);
37918     },
37919 /*
37920     getWidth : function(){
37921         return this.inner.getWidth();
37922     },
37923
37924     setWidth : function(width){
37925         var iwidth = width - this.pnode.getPadding("lr");
37926         this.inner.setWidth(iwidth);
37927         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37928         this.pnode.setWidth(width);
37929     },
37930 */
37931     /**
37932      * Show or hide the tab
37933      * @param {Boolean} hidden True to hide or false to show.
37934      */
37935     setHidden : function(hidden){
37936         this.hidden = hidden;
37937         this.pnode.setStyle("display", hidden ? "none" : "");
37938     },
37939
37940     /**
37941      * Returns true if this tab is "hidden"
37942      * @return {Boolean}
37943      */
37944     isHidden : function(){
37945         return this.hidden;
37946     },
37947
37948     /**
37949      * Returns the text for this tab
37950      * @return {String}
37951      */
37952     getText : function(){
37953         return this.text;
37954     },
37955     /*
37956     autoSize : function(){
37957         //this.el.beginMeasure();
37958         this.textEl.setWidth(1);
37959         /*
37960          *  #2804 [new] Tabs in Roojs
37961          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37962          */
37963         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37964         //this.el.endMeasure();
37965     //},
37966
37967     /**
37968      * Sets the text for the tab (Note: this also sets the tooltip text)
37969      * @param {String} text The tab's text and tooltip
37970      */
37971     setText : function(text){
37972         this.text = text;
37973         this.textEl.update(text);
37974         this.setTooltip(text);
37975         //if(!this.tabPanel.resizeTabs){
37976         //    this.autoSize();
37977         //}
37978     },
37979     /**
37980      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37981      */
37982     activate : function(){
37983         this.tabPanel.activate(this.id);
37984     },
37985
37986     /**
37987      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37988      */
37989     disable : function(){
37990         if(this.tabPanel.active != this){
37991             this.disabled = true;
37992             this.pnode.addClass("disabled");
37993         }
37994     },
37995
37996     /**
37997      * Enables this TabPanelItem if it was previously disabled.
37998      */
37999     enable : function(){
38000         this.disabled = false;
38001         this.pnode.removeClass("disabled");
38002     },
38003
38004     /**
38005      * Sets the content for this TabPanelItem.
38006      * @param {String} content The content
38007      * @param {Boolean} loadScripts true to look for and load scripts
38008      */
38009     setContent : function(content, loadScripts){
38010         this.bodyEl.update(content, loadScripts);
38011     },
38012
38013     /**
38014      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38015      * @return {Roo.UpdateManager} The UpdateManager
38016      */
38017     getUpdateManager : function(){
38018         return this.bodyEl.getUpdateManager();
38019     },
38020
38021     /**
38022      * Set a URL to be used to load the content for this TabPanelItem.
38023      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38024      * @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)
38025      * @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)
38026      * @return {Roo.UpdateManager} The UpdateManager
38027      */
38028     setUrl : function(url, params, loadOnce){
38029         if(this.refreshDelegate){
38030             this.un('activate', this.refreshDelegate);
38031         }
38032         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38033         this.on("activate", this.refreshDelegate);
38034         return this.bodyEl.getUpdateManager();
38035     },
38036
38037     /** @private */
38038     _handleRefresh : function(url, params, loadOnce){
38039         if(!loadOnce || !this.loaded){
38040             var updater = this.bodyEl.getUpdateManager();
38041             updater.update(url, params, this._setLoaded.createDelegate(this));
38042         }
38043     },
38044
38045     /**
38046      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38047      *   Will fail silently if the setUrl method has not been called.
38048      *   This does not activate the panel, just updates its content.
38049      */
38050     refresh : function(){
38051         if(this.refreshDelegate){
38052            this.loaded = false;
38053            this.refreshDelegate();
38054         }
38055     },
38056
38057     /** @private */
38058     _setLoaded : function(){
38059         this.loaded = true;
38060     },
38061
38062     /** @private */
38063     closeClick : function(e){
38064         var o = {};
38065         e.stopEvent();
38066         this.fireEvent("beforeclose", this, o);
38067         if(o.cancel !== true){
38068             this.tabPanel.removeTab(this.id);
38069         }
38070     },
38071     /**
38072      * The text displayed in the tooltip for the close icon.
38073      * @type String
38074      */
38075     closeText : "Close this tab"
38076 });
38077 /**
38078 *    This script refer to:
38079 *    Title: International Telephone Input
38080 *    Author: Jack O'Connor
38081 *    Code version:  v12.1.12
38082 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38083 **/
38084
38085 Roo.bootstrap.PhoneInputData = function() {
38086     var d = [
38087       [
38088         "Afghanistan (‫افغانستان‬‎)",
38089         "af",
38090         "93"
38091       ],
38092       [
38093         "Albania (Shqipëri)",
38094         "al",
38095         "355"
38096       ],
38097       [
38098         "Algeria (‫الجزائر‬‎)",
38099         "dz",
38100         "213"
38101       ],
38102       [
38103         "American Samoa",
38104         "as",
38105         "1684"
38106       ],
38107       [
38108         "Andorra",
38109         "ad",
38110         "376"
38111       ],
38112       [
38113         "Angola",
38114         "ao",
38115         "244"
38116       ],
38117       [
38118         "Anguilla",
38119         "ai",
38120         "1264"
38121       ],
38122       [
38123         "Antigua and Barbuda",
38124         "ag",
38125         "1268"
38126       ],
38127       [
38128         "Argentina",
38129         "ar",
38130         "54"
38131       ],
38132       [
38133         "Armenia (Հայաստան)",
38134         "am",
38135         "374"
38136       ],
38137       [
38138         "Aruba",
38139         "aw",
38140         "297"
38141       ],
38142       [
38143         "Australia",
38144         "au",
38145         "61",
38146         0
38147       ],
38148       [
38149         "Austria (Österreich)",
38150         "at",
38151         "43"
38152       ],
38153       [
38154         "Azerbaijan (Azərbaycan)",
38155         "az",
38156         "994"
38157       ],
38158       [
38159         "Bahamas",
38160         "bs",
38161         "1242"
38162       ],
38163       [
38164         "Bahrain (‫البحرين‬‎)",
38165         "bh",
38166         "973"
38167       ],
38168       [
38169         "Bangladesh (বাংলাদেশ)",
38170         "bd",
38171         "880"
38172       ],
38173       [
38174         "Barbados",
38175         "bb",
38176         "1246"
38177       ],
38178       [
38179         "Belarus (Беларусь)",
38180         "by",
38181         "375"
38182       ],
38183       [
38184         "Belgium (België)",
38185         "be",
38186         "32"
38187       ],
38188       [
38189         "Belize",
38190         "bz",
38191         "501"
38192       ],
38193       [
38194         "Benin (Bénin)",
38195         "bj",
38196         "229"
38197       ],
38198       [
38199         "Bermuda",
38200         "bm",
38201         "1441"
38202       ],
38203       [
38204         "Bhutan (འབྲུག)",
38205         "bt",
38206         "975"
38207       ],
38208       [
38209         "Bolivia",
38210         "bo",
38211         "591"
38212       ],
38213       [
38214         "Bosnia and Herzegovina (Босна и Херцеговина)",
38215         "ba",
38216         "387"
38217       ],
38218       [
38219         "Botswana",
38220         "bw",
38221         "267"
38222       ],
38223       [
38224         "Brazil (Brasil)",
38225         "br",
38226         "55"
38227       ],
38228       [
38229         "British Indian Ocean Territory",
38230         "io",
38231         "246"
38232       ],
38233       [
38234         "British Virgin Islands",
38235         "vg",
38236         "1284"
38237       ],
38238       [
38239         "Brunei",
38240         "bn",
38241         "673"
38242       ],
38243       [
38244         "Bulgaria (България)",
38245         "bg",
38246         "359"
38247       ],
38248       [
38249         "Burkina Faso",
38250         "bf",
38251         "226"
38252       ],
38253       [
38254         "Burundi (Uburundi)",
38255         "bi",
38256         "257"
38257       ],
38258       [
38259         "Cambodia (កម្ពុជា)",
38260         "kh",
38261         "855"
38262       ],
38263       [
38264         "Cameroon (Cameroun)",
38265         "cm",
38266         "237"
38267       ],
38268       [
38269         "Canada",
38270         "ca",
38271         "1",
38272         1,
38273         ["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"]
38274       ],
38275       [
38276         "Cape Verde (Kabu Verdi)",
38277         "cv",
38278         "238"
38279       ],
38280       [
38281         "Caribbean Netherlands",
38282         "bq",
38283         "599",
38284         1
38285       ],
38286       [
38287         "Cayman Islands",
38288         "ky",
38289         "1345"
38290       ],
38291       [
38292         "Central African Republic (République centrafricaine)",
38293         "cf",
38294         "236"
38295       ],
38296       [
38297         "Chad (Tchad)",
38298         "td",
38299         "235"
38300       ],
38301       [
38302         "Chile",
38303         "cl",
38304         "56"
38305       ],
38306       [
38307         "China (中国)",
38308         "cn",
38309         "86"
38310       ],
38311       [
38312         "Christmas Island",
38313         "cx",
38314         "61",
38315         2
38316       ],
38317       [
38318         "Cocos (Keeling) Islands",
38319         "cc",
38320         "61",
38321         1
38322       ],
38323       [
38324         "Colombia",
38325         "co",
38326         "57"
38327       ],
38328       [
38329         "Comoros (‫جزر القمر‬‎)",
38330         "km",
38331         "269"
38332       ],
38333       [
38334         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38335         "cd",
38336         "243"
38337       ],
38338       [
38339         "Congo (Republic) (Congo-Brazzaville)",
38340         "cg",
38341         "242"
38342       ],
38343       [
38344         "Cook Islands",
38345         "ck",
38346         "682"
38347       ],
38348       [
38349         "Costa Rica",
38350         "cr",
38351         "506"
38352       ],
38353       [
38354         "Côte d’Ivoire",
38355         "ci",
38356         "225"
38357       ],
38358       [
38359         "Croatia (Hrvatska)",
38360         "hr",
38361         "385"
38362       ],
38363       [
38364         "Cuba",
38365         "cu",
38366         "53"
38367       ],
38368       [
38369         "Curaçao",
38370         "cw",
38371         "599",
38372         0
38373       ],
38374       [
38375         "Cyprus (Κύπρος)",
38376         "cy",
38377         "357"
38378       ],
38379       [
38380         "Czech Republic (Česká republika)",
38381         "cz",
38382         "420"
38383       ],
38384       [
38385         "Denmark (Danmark)",
38386         "dk",
38387         "45"
38388       ],
38389       [
38390         "Djibouti",
38391         "dj",
38392         "253"
38393       ],
38394       [
38395         "Dominica",
38396         "dm",
38397         "1767"
38398       ],
38399       [
38400         "Dominican Republic (República Dominicana)",
38401         "do",
38402         "1",
38403         2,
38404         ["809", "829", "849"]
38405       ],
38406       [
38407         "Ecuador",
38408         "ec",
38409         "593"
38410       ],
38411       [
38412         "Egypt (‫مصر‬‎)",
38413         "eg",
38414         "20"
38415       ],
38416       [
38417         "El Salvador",
38418         "sv",
38419         "503"
38420       ],
38421       [
38422         "Equatorial Guinea (Guinea Ecuatorial)",
38423         "gq",
38424         "240"
38425       ],
38426       [
38427         "Eritrea",
38428         "er",
38429         "291"
38430       ],
38431       [
38432         "Estonia (Eesti)",
38433         "ee",
38434         "372"
38435       ],
38436       [
38437         "Ethiopia",
38438         "et",
38439         "251"
38440       ],
38441       [
38442         "Falkland Islands (Islas Malvinas)",
38443         "fk",
38444         "500"
38445       ],
38446       [
38447         "Faroe Islands (Føroyar)",
38448         "fo",
38449         "298"
38450       ],
38451       [
38452         "Fiji",
38453         "fj",
38454         "679"
38455       ],
38456       [
38457         "Finland (Suomi)",
38458         "fi",
38459         "358",
38460         0
38461       ],
38462       [
38463         "France",
38464         "fr",
38465         "33"
38466       ],
38467       [
38468         "French Guiana (Guyane française)",
38469         "gf",
38470         "594"
38471       ],
38472       [
38473         "French Polynesia (Polynésie française)",
38474         "pf",
38475         "689"
38476       ],
38477       [
38478         "Gabon",
38479         "ga",
38480         "241"
38481       ],
38482       [
38483         "Gambia",
38484         "gm",
38485         "220"
38486       ],
38487       [
38488         "Georgia (საქართველო)",
38489         "ge",
38490         "995"
38491       ],
38492       [
38493         "Germany (Deutschland)",
38494         "de",
38495         "49"
38496       ],
38497       [
38498         "Ghana (Gaana)",
38499         "gh",
38500         "233"
38501       ],
38502       [
38503         "Gibraltar",
38504         "gi",
38505         "350"
38506       ],
38507       [
38508         "Greece (Ελλάδα)",
38509         "gr",
38510         "30"
38511       ],
38512       [
38513         "Greenland (Kalaallit Nunaat)",
38514         "gl",
38515         "299"
38516       ],
38517       [
38518         "Grenada",
38519         "gd",
38520         "1473"
38521       ],
38522       [
38523         "Guadeloupe",
38524         "gp",
38525         "590",
38526         0
38527       ],
38528       [
38529         "Guam",
38530         "gu",
38531         "1671"
38532       ],
38533       [
38534         "Guatemala",
38535         "gt",
38536         "502"
38537       ],
38538       [
38539         "Guernsey",
38540         "gg",
38541         "44",
38542         1
38543       ],
38544       [
38545         "Guinea (Guinée)",
38546         "gn",
38547         "224"
38548       ],
38549       [
38550         "Guinea-Bissau (Guiné Bissau)",
38551         "gw",
38552         "245"
38553       ],
38554       [
38555         "Guyana",
38556         "gy",
38557         "592"
38558       ],
38559       [
38560         "Haiti",
38561         "ht",
38562         "509"
38563       ],
38564       [
38565         "Honduras",
38566         "hn",
38567         "504"
38568       ],
38569       [
38570         "Hong Kong (香港)",
38571         "hk",
38572         "852"
38573       ],
38574       [
38575         "Hungary (Magyarország)",
38576         "hu",
38577         "36"
38578       ],
38579       [
38580         "Iceland (Ísland)",
38581         "is",
38582         "354"
38583       ],
38584       [
38585         "India (भारत)",
38586         "in",
38587         "91"
38588       ],
38589       [
38590         "Indonesia",
38591         "id",
38592         "62"
38593       ],
38594       [
38595         "Iran (‫ایران‬‎)",
38596         "ir",
38597         "98"
38598       ],
38599       [
38600         "Iraq (‫العراق‬‎)",
38601         "iq",
38602         "964"
38603       ],
38604       [
38605         "Ireland",
38606         "ie",
38607         "353"
38608       ],
38609       [
38610         "Isle of Man",
38611         "im",
38612         "44",
38613         2
38614       ],
38615       [
38616         "Israel (‫ישראל‬‎)",
38617         "il",
38618         "972"
38619       ],
38620       [
38621         "Italy (Italia)",
38622         "it",
38623         "39",
38624         0
38625       ],
38626       [
38627         "Jamaica",
38628         "jm",
38629         "1876"
38630       ],
38631       [
38632         "Japan (日本)",
38633         "jp",
38634         "81"
38635       ],
38636       [
38637         "Jersey",
38638         "je",
38639         "44",
38640         3
38641       ],
38642       [
38643         "Jordan (‫الأردن‬‎)",
38644         "jo",
38645         "962"
38646       ],
38647       [
38648         "Kazakhstan (Казахстан)",
38649         "kz",
38650         "7",
38651         1
38652       ],
38653       [
38654         "Kenya",
38655         "ke",
38656         "254"
38657       ],
38658       [
38659         "Kiribati",
38660         "ki",
38661         "686"
38662       ],
38663       [
38664         "Kosovo",
38665         "xk",
38666         "383"
38667       ],
38668       [
38669         "Kuwait (‫الكويت‬‎)",
38670         "kw",
38671         "965"
38672       ],
38673       [
38674         "Kyrgyzstan (Кыргызстан)",
38675         "kg",
38676         "996"
38677       ],
38678       [
38679         "Laos (ລາວ)",
38680         "la",
38681         "856"
38682       ],
38683       [
38684         "Latvia (Latvija)",
38685         "lv",
38686         "371"
38687       ],
38688       [
38689         "Lebanon (‫لبنان‬‎)",
38690         "lb",
38691         "961"
38692       ],
38693       [
38694         "Lesotho",
38695         "ls",
38696         "266"
38697       ],
38698       [
38699         "Liberia",
38700         "lr",
38701         "231"
38702       ],
38703       [
38704         "Libya (‫ليبيا‬‎)",
38705         "ly",
38706         "218"
38707       ],
38708       [
38709         "Liechtenstein",
38710         "li",
38711         "423"
38712       ],
38713       [
38714         "Lithuania (Lietuva)",
38715         "lt",
38716         "370"
38717       ],
38718       [
38719         "Luxembourg",
38720         "lu",
38721         "352"
38722       ],
38723       [
38724         "Macau (澳門)",
38725         "mo",
38726         "853"
38727       ],
38728       [
38729         "Macedonia (FYROM) (Македонија)",
38730         "mk",
38731         "389"
38732       ],
38733       [
38734         "Madagascar (Madagasikara)",
38735         "mg",
38736         "261"
38737       ],
38738       [
38739         "Malawi",
38740         "mw",
38741         "265"
38742       ],
38743       [
38744         "Malaysia",
38745         "my",
38746         "60"
38747       ],
38748       [
38749         "Maldives",
38750         "mv",
38751         "960"
38752       ],
38753       [
38754         "Mali",
38755         "ml",
38756         "223"
38757       ],
38758       [
38759         "Malta",
38760         "mt",
38761         "356"
38762       ],
38763       [
38764         "Marshall Islands",
38765         "mh",
38766         "692"
38767       ],
38768       [
38769         "Martinique",
38770         "mq",
38771         "596"
38772       ],
38773       [
38774         "Mauritania (‫موريتانيا‬‎)",
38775         "mr",
38776         "222"
38777       ],
38778       [
38779         "Mauritius (Moris)",
38780         "mu",
38781         "230"
38782       ],
38783       [
38784         "Mayotte",
38785         "yt",
38786         "262",
38787         1
38788       ],
38789       [
38790         "Mexico (México)",
38791         "mx",
38792         "52"
38793       ],
38794       [
38795         "Micronesia",
38796         "fm",
38797         "691"
38798       ],
38799       [
38800         "Moldova (Republica Moldova)",
38801         "md",
38802         "373"
38803       ],
38804       [
38805         "Monaco",
38806         "mc",
38807         "377"
38808       ],
38809       [
38810         "Mongolia (Монгол)",
38811         "mn",
38812         "976"
38813       ],
38814       [
38815         "Montenegro (Crna Gora)",
38816         "me",
38817         "382"
38818       ],
38819       [
38820         "Montserrat",
38821         "ms",
38822         "1664"
38823       ],
38824       [
38825         "Morocco (‫المغرب‬‎)",
38826         "ma",
38827         "212",
38828         0
38829       ],
38830       [
38831         "Mozambique (Moçambique)",
38832         "mz",
38833         "258"
38834       ],
38835       [
38836         "Myanmar (Burma) (မြန်မာ)",
38837         "mm",
38838         "95"
38839       ],
38840       [
38841         "Namibia (Namibië)",
38842         "na",
38843         "264"
38844       ],
38845       [
38846         "Nauru",
38847         "nr",
38848         "674"
38849       ],
38850       [
38851         "Nepal (नेपाल)",
38852         "np",
38853         "977"
38854       ],
38855       [
38856         "Netherlands (Nederland)",
38857         "nl",
38858         "31"
38859       ],
38860       [
38861         "New Caledonia (Nouvelle-Calédonie)",
38862         "nc",
38863         "687"
38864       ],
38865       [
38866         "New Zealand",
38867         "nz",
38868         "64"
38869       ],
38870       [
38871         "Nicaragua",
38872         "ni",
38873         "505"
38874       ],
38875       [
38876         "Niger (Nijar)",
38877         "ne",
38878         "227"
38879       ],
38880       [
38881         "Nigeria",
38882         "ng",
38883         "234"
38884       ],
38885       [
38886         "Niue",
38887         "nu",
38888         "683"
38889       ],
38890       [
38891         "Norfolk Island",
38892         "nf",
38893         "672"
38894       ],
38895       [
38896         "North Korea (조선 민주주의 인민 공화국)",
38897         "kp",
38898         "850"
38899       ],
38900       [
38901         "Northern Mariana Islands",
38902         "mp",
38903         "1670"
38904       ],
38905       [
38906         "Norway (Norge)",
38907         "no",
38908         "47",
38909         0
38910       ],
38911       [
38912         "Oman (‫عُمان‬‎)",
38913         "om",
38914         "968"
38915       ],
38916       [
38917         "Pakistan (‫پاکستان‬‎)",
38918         "pk",
38919         "92"
38920       ],
38921       [
38922         "Palau",
38923         "pw",
38924         "680"
38925       ],
38926       [
38927         "Palestine (‫فلسطين‬‎)",
38928         "ps",
38929         "970"
38930       ],
38931       [
38932         "Panama (Panamá)",
38933         "pa",
38934         "507"
38935       ],
38936       [
38937         "Papua New Guinea",
38938         "pg",
38939         "675"
38940       ],
38941       [
38942         "Paraguay",
38943         "py",
38944         "595"
38945       ],
38946       [
38947         "Peru (Perú)",
38948         "pe",
38949         "51"
38950       ],
38951       [
38952         "Philippines",
38953         "ph",
38954         "63"
38955       ],
38956       [
38957         "Poland (Polska)",
38958         "pl",
38959         "48"
38960       ],
38961       [
38962         "Portugal",
38963         "pt",
38964         "351"
38965       ],
38966       [
38967         "Puerto Rico",
38968         "pr",
38969         "1",
38970         3,
38971         ["787", "939"]
38972       ],
38973       [
38974         "Qatar (‫قطر‬‎)",
38975         "qa",
38976         "974"
38977       ],
38978       [
38979         "Réunion (La Réunion)",
38980         "re",
38981         "262",
38982         0
38983       ],
38984       [
38985         "Romania (România)",
38986         "ro",
38987         "40"
38988       ],
38989       [
38990         "Russia (Россия)",
38991         "ru",
38992         "7",
38993         0
38994       ],
38995       [
38996         "Rwanda",
38997         "rw",
38998         "250"
38999       ],
39000       [
39001         "Saint Barthélemy",
39002         "bl",
39003         "590",
39004         1
39005       ],
39006       [
39007         "Saint Helena",
39008         "sh",
39009         "290"
39010       ],
39011       [
39012         "Saint Kitts and Nevis",
39013         "kn",
39014         "1869"
39015       ],
39016       [
39017         "Saint Lucia",
39018         "lc",
39019         "1758"
39020       ],
39021       [
39022         "Saint Martin (Saint-Martin (partie française))",
39023         "mf",
39024         "590",
39025         2
39026       ],
39027       [
39028         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39029         "pm",
39030         "508"
39031       ],
39032       [
39033         "Saint Vincent and the Grenadines",
39034         "vc",
39035         "1784"
39036       ],
39037       [
39038         "Samoa",
39039         "ws",
39040         "685"
39041       ],
39042       [
39043         "San Marino",
39044         "sm",
39045         "378"
39046       ],
39047       [
39048         "São Tomé and Príncipe (São Tomé e Príncipe)",
39049         "st",
39050         "239"
39051       ],
39052       [
39053         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39054         "sa",
39055         "966"
39056       ],
39057       [
39058         "Senegal (Sénégal)",
39059         "sn",
39060         "221"
39061       ],
39062       [
39063         "Serbia (Србија)",
39064         "rs",
39065         "381"
39066       ],
39067       [
39068         "Seychelles",
39069         "sc",
39070         "248"
39071       ],
39072       [
39073         "Sierra Leone",
39074         "sl",
39075         "232"
39076       ],
39077       [
39078         "Singapore",
39079         "sg",
39080         "65"
39081       ],
39082       [
39083         "Sint Maarten",
39084         "sx",
39085         "1721"
39086       ],
39087       [
39088         "Slovakia (Slovensko)",
39089         "sk",
39090         "421"
39091       ],
39092       [
39093         "Slovenia (Slovenija)",
39094         "si",
39095         "386"
39096       ],
39097       [
39098         "Solomon Islands",
39099         "sb",
39100         "677"
39101       ],
39102       [
39103         "Somalia (Soomaaliya)",
39104         "so",
39105         "252"
39106       ],
39107       [
39108         "South Africa",
39109         "za",
39110         "27"
39111       ],
39112       [
39113         "South Korea (대한민국)",
39114         "kr",
39115         "82"
39116       ],
39117       [
39118         "South Sudan (‫جنوب السودان‬‎)",
39119         "ss",
39120         "211"
39121       ],
39122       [
39123         "Spain (España)",
39124         "es",
39125         "34"
39126       ],
39127       [
39128         "Sri Lanka (ශ්‍රී ලංකාව)",
39129         "lk",
39130         "94"
39131       ],
39132       [
39133         "Sudan (‫السودان‬‎)",
39134         "sd",
39135         "249"
39136       ],
39137       [
39138         "Suriname",
39139         "sr",
39140         "597"
39141       ],
39142       [
39143         "Svalbard and Jan Mayen",
39144         "sj",
39145         "47",
39146         1
39147       ],
39148       [
39149         "Swaziland",
39150         "sz",
39151         "268"
39152       ],
39153       [
39154         "Sweden (Sverige)",
39155         "se",
39156         "46"
39157       ],
39158       [
39159         "Switzerland (Schweiz)",
39160         "ch",
39161         "41"
39162       ],
39163       [
39164         "Syria (‫سوريا‬‎)",
39165         "sy",
39166         "963"
39167       ],
39168       [
39169         "Taiwan (台灣)",
39170         "tw",
39171         "886"
39172       ],
39173       [
39174         "Tajikistan",
39175         "tj",
39176         "992"
39177       ],
39178       [
39179         "Tanzania",
39180         "tz",
39181         "255"
39182       ],
39183       [
39184         "Thailand (ไทย)",
39185         "th",
39186         "66"
39187       ],
39188       [
39189         "Timor-Leste",
39190         "tl",
39191         "670"
39192       ],
39193       [
39194         "Togo",
39195         "tg",
39196         "228"
39197       ],
39198       [
39199         "Tokelau",
39200         "tk",
39201         "690"
39202       ],
39203       [
39204         "Tonga",
39205         "to",
39206         "676"
39207       ],
39208       [
39209         "Trinidad and Tobago",
39210         "tt",
39211         "1868"
39212       ],
39213       [
39214         "Tunisia (‫تونس‬‎)",
39215         "tn",
39216         "216"
39217       ],
39218       [
39219         "Turkey (Türkiye)",
39220         "tr",
39221         "90"
39222       ],
39223       [
39224         "Turkmenistan",
39225         "tm",
39226         "993"
39227       ],
39228       [
39229         "Turks and Caicos Islands",
39230         "tc",
39231         "1649"
39232       ],
39233       [
39234         "Tuvalu",
39235         "tv",
39236         "688"
39237       ],
39238       [
39239         "U.S. Virgin Islands",
39240         "vi",
39241         "1340"
39242       ],
39243       [
39244         "Uganda",
39245         "ug",
39246         "256"
39247       ],
39248       [
39249         "Ukraine (Україна)",
39250         "ua",
39251         "380"
39252       ],
39253       [
39254         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39255         "ae",
39256         "971"
39257       ],
39258       [
39259         "United Kingdom",
39260         "gb",
39261         "44",
39262         0
39263       ],
39264       [
39265         "United States",
39266         "us",
39267         "1",
39268         0
39269       ],
39270       [
39271         "Uruguay",
39272         "uy",
39273         "598"
39274       ],
39275       [
39276         "Uzbekistan (Oʻzbekiston)",
39277         "uz",
39278         "998"
39279       ],
39280       [
39281         "Vanuatu",
39282         "vu",
39283         "678"
39284       ],
39285       [
39286         "Vatican City (Città del Vaticano)",
39287         "va",
39288         "39",
39289         1
39290       ],
39291       [
39292         "Venezuela",
39293         "ve",
39294         "58"
39295       ],
39296       [
39297         "Vietnam (Việt Nam)",
39298         "vn",
39299         "84"
39300       ],
39301       [
39302         "Wallis and Futuna (Wallis-et-Futuna)",
39303         "wf",
39304         "681"
39305       ],
39306       [
39307         "Western Sahara (‫الصحراء الغربية‬‎)",
39308         "eh",
39309         "212",
39310         1
39311       ],
39312       [
39313         "Yemen (‫اليمن‬‎)",
39314         "ye",
39315         "967"
39316       ],
39317       [
39318         "Zambia",
39319         "zm",
39320         "260"
39321       ],
39322       [
39323         "Zimbabwe",
39324         "zw",
39325         "263"
39326       ],
39327       [
39328         "Åland Islands",
39329         "ax",
39330         "358",
39331         1
39332       ]
39333   ];
39334   
39335   return d;
39336 }/**
39337 *    This script refer to:
39338 *    Title: International Telephone Input
39339 *    Author: Jack O'Connor
39340 *    Code version:  v12.1.12
39341 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39342 **/
39343
39344 /**
39345  * @class Roo.bootstrap.PhoneInput
39346  * @extends Roo.bootstrap.TriggerField
39347  * An input with International dial-code selection
39348  
39349  * @cfg {String} defaultDialCode default '+852'
39350  * @cfg {Array} preferedCountries default []
39351   
39352  * @constructor
39353  * Create a new PhoneInput.
39354  * @param {Object} config Configuration options
39355  */
39356
39357 Roo.bootstrap.PhoneInput = function(config) {
39358     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39359 };
39360
39361 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39362         
39363         listWidth: undefined,
39364         
39365         selectedClass: 'active',
39366         
39367         invalidClass : "has-warning",
39368         
39369         validClass: 'has-success',
39370         
39371         allowed: '0123456789',
39372         
39373         /**
39374          * @cfg {String} defaultDialCode The default dial code when initializing the input
39375          */
39376         defaultDialCode: '+852',
39377         
39378         /**
39379          * @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
39380          */
39381         preferedCountries: false,
39382         
39383         getAutoCreate : function()
39384         {
39385             var data = Roo.bootstrap.PhoneInputData();
39386             var align = this.labelAlign || this.parentLabelAlign();
39387             var id = Roo.id();
39388             
39389             this.allCountries = [];
39390             this.dialCodeMapping = [];
39391             
39392             for (var i = 0; i < data.length; i++) {
39393               var c = data[i];
39394               this.allCountries[i] = {
39395                 name: c[0],
39396                 iso2: c[1],
39397                 dialCode: c[2],
39398                 priority: c[3] || 0,
39399                 areaCodes: c[4] || null
39400               };
39401               this.dialCodeMapping[c[2]] = {
39402                   name: c[0],
39403                   iso2: c[1],
39404                   priority: c[3] || 0,
39405                   areaCodes: c[4] || null
39406               };
39407             }
39408             
39409             var cfg = {
39410                 cls: 'form-group',
39411                 cn: []
39412             };
39413             
39414             var input =  {
39415                 tag: 'input',
39416                 id : id,
39417                 cls : 'form-control tel-input',
39418                 autocomplete: 'new-password'
39419             };
39420             
39421             var hiddenInput = {
39422                 tag: 'input',
39423                 type: 'hidden',
39424                 cls: 'hidden-tel-input'
39425             };
39426             
39427             if (this.name) {
39428                 hiddenInput.name = this.name;
39429             }
39430             
39431             if (this.disabled) {
39432                 input.disabled = true;
39433             }
39434             
39435             var flag_container = {
39436                 tag: 'div',
39437                 cls: 'flag-box',
39438                 cn: [
39439                     {
39440                         tag: 'div',
39441                         cls: 'flag'
39442                     },
39443                     {
39444                         tag: 'div',
39445                         cls: 'caret'
39446                     }
39447                 ]
39448             };
39449             
39450             var box = {
39451                 tag: 'div',
39452                 cls: this.hasFeedback ? 'has-feedback' : '',
39453                 cn: [
39454                     hiddenInput,
39455                     input,
39456                     {
39457                         tag: 'input',
39458                         cls: 'dial-code-holder',
39459                         disabled: true
39460                     }
39461                 ]
39462             };
39463             
39464             var container = {
39465                 cls: 'roo-select2-container input-group',
39466                 cn: [
39467                     flag_container,
39468                     box
39469                 ]
39470             };
39471             
39472             if (this.fieldLabel.length) {
39473                 var indicator = {
39474                     tag: 'i',
39475                     tooltip: 'This field is required'
39476                 };
39477                 
39478                 var label = {
39479                     tag: 'label',
39480                     'for':  id,
39481                     cls: 'control-label',
39482                     cn: []
39483                 };
39484                 
39485                 var label_text = {
39486                     tag: 'span',
39487                     html: this.fieldLabel
39488                 };
39489                 
39490                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39491                 label.cn = [
39492                     indicator,
39493                     label_text
39494                 ];
39495                 
39496                 if(this.indicatorpos == 'right') {
39497                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39498                     label.cn = [
39499                         label_text,
39500                         indicator
39501                     ];
39502                 }
39503                 
39504                 if(align == 'left') {
39505                     container = {
39506                         tag: 'div',
39507                         cn: [
39508                             container
39509                         ]
39510                     };
39511                     
39512                     if(this.labelWidth > 12){
39513                         label.style = "width: " + this.labelWidth + 'px';
39514                     }
39515                     if(this.labelWidth < 13 && this.labelmd == 0){
39516                         this.labelmd = this.labelWidth;
39517                     }
39518                     if(this.labellg > 0){
39519                         label.cls += ' col-lg-' + this.labellg;
39520                         input.cls += ' col-lg-' + (12 - this.labellg);
39521                     }
39522                     if(this.labelmd > 0){
39523                         label.cls += ' col-md-' + this.labelmd;
39524                         container.cls += ' col-md-' + (12 - this.labelmd);
39525                     }
39526                     if(this.labelsm > 0){
39527                         label.cls += ' col-sm-' + this.labelsm;
39528                         container.cls += ' col-sm-' + (12 - this.labelsm);
39529                     }
39530                     if(this.labelxs > 0){
39531                         label.cls += ' col-xs-' + this.labelxs;
39532                         container.cls += ' col-xs-' + (12 - this.labelxs);
39533                     }
39534                 }
39535             }
39536             
39537             cfg.cn = [
39538                 label,
39539                 container
39540             ];
39541             
39542             var settings = this;
39543             
39544             ['xs','sm','md','lg'].map(function(size){
39545                 if (settings[size]) {
39546                     cfg.cls += ' col-' + size + '-' + settings[size];
39547                 }
39548             });
39549             
39550             this.store = new Roo.data.Store({
39551                 proxy : new Roo.data.MemoryProxy({}),
39552                 reader : new Roo.data.JsonReader({
39553                     fields : [
39554                         {
39555                             'name' : 'name',
39556                             'type' : 'string'
39557                         },
39558                         {
39559                             'name' : 'iso2',
39560                             'type' : 'string'
39561                         },
39562                         {
39563                             'name' : 'dialCode',
39564                             'type' : 'string'
39565                         },
39566                         {
39567                             'name' : 'priority',
39568                             'type' : 'string'
39569                         },
39570                         {
39571                             'name' : 'areaCodes',
39572                             'type' : 'string'
39573                         }
39574                     ]
39575                 })
39576             });
39577             
39578             if(!this.preferedCountries) {
39579                 this.preferedCountries = [
39580                     'hk',
39581                     'gb',
39582                     'us'
39583                 ];
39584             }
39585             
39586             var p = this.preferedCountries.reverse();
39587             
39588             if(p) {
39589                 for (var i = 0; i < p.length; i++) {
39590                     for (var j = 0; j < this.allCountries.length; j++) {
39591                         if(this.allCountries[j].iso2 == p[i]) {
39592                             var t = this.allCountries[j];
39593                             this.allCountries.splice(j,1);
39594                             this.allCountries.unshift(t);
39595                         }
39596                     } 
39597                 }
39598             }
39599             
39600             this.store.proxy.data = {
39601                 success: true,
39602                 data: this.allCountries
39603             };
39604             
39605             return cfg;
39606         },
39607         
39608         initEvents : function()
39609         {
39610             this.createList();
39611             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39612             
39613             this.indicator = this.indicatorEl();
39614             this.flag = this.flagEl();
39615             this.dialCodeHolder = this.dialCodeHolderEl();
39616             
39617             this.trigger = this.el.select('div.flag-box',true).first();
39618             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39619             
39620             var _this = this;
39621             
39622             (function(){
39623                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39624                 _this.list.setWidth(lw);
39625             }).defer(100);
39626             
39627             this.list.on('mouseover', this.onViewOver, this);
39628             this.list.on('mousemove', this.onViewMove, this);
39629             this.inputEl().on("keyup", this.onKeyUp, this);
39630             
39631             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39632
39633             this.view = new Roo.View(this.list, this.tpl, {
39634                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39635             });
39636             
39637             this.view.on('click', this.onViewClick, this);
39638             this.setValue(this.defaultDialCode);
39639         },
39640         
39641         onTriggerClick : function(e)
39642         {
39643             Roo.log('trigger click');
39644             if(this.disabled){
39645                 return;
39646             }
39647             
39648             if(this.isExpanded()){
39649                 this.collapse();
39650                 this.hasFocus = false;
39651             }else {
39652                 this.store.load({});
39653                 this.hasFocus = true;
39654                 this.expand();
39655             }
39656         },
39657         
39658         isExpanded : function()
39659         {
39660             return this.list.isVisible();
39661         },
39662         
39663         collapse : function()
39664         {
39665             if(!this.isExpanded()){
39666                 return;
39667             }
39668             this.list.hide();
39669             Roo.get(document).un('mousedown', this.collapseIf, this);
39670             Roo.get(document).un('mousewheel', this.collapseIf, this);
39671             this.fireEvent('collapse', this);
39672             this.validate();
39673         },
39674         
39675         expand : function()
39676         {
39677             Roo.log('expand');
39678
39679             if(this.isExpanded() || !this.hasFocus){
39680                 return;
39681             }
39682             
39683             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39684             this.list.setWidth(lw);
39685             
39686             this.list.show();
39687             this.restrictHeight();
39688             
39689             Roo.get(document).on('mousedown', this.collapseIf, this);
39690             Roo.get(document).on('mousewheel', this.collapseIf, this);
39691             
39692             this.fireEvent('expand', this);
39693         },
39694         
39695         restrictHeight : function()
39696         {
39697             this.list.alignTo(this.inputEl(), this.listAlign);
39698             this.list.alignTo(this.inputEl(), this.listAlign);
39699         },
39700         
39701         onViewOver : function(e, t)
39702         {
39703             if(this.inKeyMode){
39704                 return;
39705             }
39706             var item = this.view.findItemFromChild(t);
39707             
39708             if(item){
39709                 var index = this.view.indexOf(item);
39710                 this.select(index, false);
39711             }
39712         },
39713
39714         // private
39715         onViewClick : function(view, doFocus, el, e)
39716         {
39717             var index = this.view.getSelectedIndexes()[0];
39718             
39719             var r = this.store.getAt(index);
39720             
39721             if(r){
39722                 this.onSelect(r, index);
39723             }
39724             if(doFocus !== false && !this.blockFocus){
39725                 this.inputEl().focus();
39726             }
39727         },
39728         
39729         onViewMove : function(e, t)
39730         {
39731             this.inKeyMode = false;
39732         },
39733         
39734         select : function(index, scrollIntoView)
39735         {
39736             this.selectedIndex = index;
39737             this.view.select(index);
39738             if(scrollIntoView !== false){
39739                 var el = this.view.getNode(index);
39740                 if(el){
39741                     this.list.scrollChildIntoView(el, false);
39742                 }
39743             }
39744         },
39745         
39746         createList : function()
39747         {
39748             this.list = Roo.get(document.body).createChild({
39749                 tag: 'ul',
39750                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39751                 style: 'display:none'
39752             });
39753             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39754         },
39755         
39756         collapseIf : function(e)
39757         {
39758             var in_combo  = e.within(this.el);
39759             var in_list =  e.within(this.list);
39760             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39761             
39762             if (in_combo || in_list || is_list) {
39763                 return;
39764             }
39765             this.collapse();
39766         },
39767         
39768         onSelect : function(record, index)
39769         {
39770             if(this.fireEvent('beforeselect', this, record, index) !== false){
39771                 
39772                 this.setFlagClass(record.data.iso2);
39773                 this.setDialCode(record.data.dialCode);
39774                 this.hasFocus = false;
39775                 this.collapse();
39776                 this.fireEvent('select', this, record, index);
39777             }
39778         },
39779         
39780         flagEl : function()
39781         {
39782             var flag = this.el.select('div.flag',true).first();
39783             if(!flag){
39784                 return false;
39785             }
39786             return flag;
39787         },
39788         
39789         dialCodeHolderEl : function()
39790         {
39791             var d = this.el.select('input.dial-code-holder',true).first();
39792             if(!d){
39793                 return false;
39794             }
39795             return d;
39796         },
39797         
39798         setDialCode : function(v)
39799         {
39800             this.dialCodeHolder.dom.value = '+'+v;
39801         },
39802         
39803         setFlagClass : function(n)
39804         {
39805             this.flag.dom.className = 'flag '+n;
39806         },
39807         
39808         getValue : function()
39809         {
39810             var v = this.inputEl().getValue();
39811             if(this.dialCodeHolder) {
39812                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39813             }
39814             return v;
39815         },
39816         
39817         setValue : function(v)
39818         {
39819             var d = this.getDialCode(v);
39820             
39821             //invalid dial code
39822             if(v.length == 0 || !d || d.length == 0) {
39823                 if(this.rendered){
39824                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39825                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39826                 }
39827                 return;
39828             }
39829             
39830             //valid dial code
39831             this.setFlagClass(this.dialCodeMapping[d].iso2);
39832             this.setDialCode(d);
39833             this.inputEl().dom.value = v.replace('+'+d,'');
39834             this.hiddenEl().dom.value = this.getValue();
39835             
39836             this.validate();
39837         },
39838         
39839         getDialCode : function(v = '')
39840         {
39841             if (v.length == 0) {
39842                 return this.dialCodeHolder.dom.value;
39843             }
39844             
39845             var dialCode = "";
39846             if (v.charAt(0) != "+") {
39847                 return false;
39848             }
39849             var numericChars = "";
39850             for (var i = 1; i < v.length; i++) {
39851               var c = v.charAt(i);
39852               if (!isNaN(c)) {
39853                 numericChars += c;
39854                 if (this.dialCodeMapping[numericChars]) {
39855                   dialCode = v.substr(1, i);
39856                 }
39857                 if (numericChars.length == 4) {
39858                   break;
39859                 }
39860               }
39861             }
39862             return dialCode;
39863         },
39864         
39865         reset : function()
39866         {
39867             this.setValue(this.defaultDialCode);
39868             this.validate();
39869         },
39870         
39871         hiddenEl : function()
39872         {
39873             return this.el.select('input.hidden-tel-input',true).first();
39874         },
39875         
39876         onKeyUp : function(e){
39877             
39878             var k = e.getKey();
39879             var c = e.getCharCode();
39880             
39881             if(
39882                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39883                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39884             ){
39885                 e.stopEvent();
39886             }
39887             
39888             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39889             //     return;
39890             // }
39891             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39892                 e.stopEvent();
39893             }
39894             
39895             this.setValue(this.getValue());
39896         }
39897         
39898 });
39899 /**
39900  * @class Roo.bootstrap.MoneyField
39901  * @extends Roo.bootstrap.ComboBox
39902  * Bootstrap MoneyField class
39903  * 
39904  * @constructor
39905  * Create a new MoneyField.
39906  * @param {Object} config Configuration options
39907  */
39908
39909 Roo.bootstrap.MoneyField = function(config) {
39910     
39911     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39912     
39913 };
39914
39915 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39916     
39917     /**
39918      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39919      */
39920     allowDecimals : true,
39921     /**
39922      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39923      */
39924     decimalSeparator : ".",
39925     /**
39926      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39927      */
39928     decimalPrecision : 2,
39929     /**
39930      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39931      */
39932     allowNegative : true,
39933     /**
39934      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39935      */
39936     minValue : Number.NEGATIVE_INFINITY,
39937     /**
39938      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39939      */
39940     maxValue : Number.MAX_VALUE,
39941     /**
39942      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39943      */
39944     minText : "The minimum value for this field is {0}",
39945     /**
39946      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39947      */
39948     maxText : "The maximum value for this field is {0}",
39949     /**
39950      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39951      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39952      */
39953     nanText : "{0} is not a valid number",
39954     /**
39955      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39956      */
39957     castInt : true,
39958     
39959     inputlg : 9,
39960     inputmd : 9,
39961     inputsm : 9,
39962     inputxs : 6,
39963     
39964     store : false,
39965     
39966     getAutoCreate : function()
39967     {
39968         var align = this.labelAlign || this.parentLabelAlign();
39969         
39970         var id = Roo.id();
39971
39972         var cfg = {
39973             cls: 'form-group',
39974             cn: []
39975         };
39976
39977         var input =  {
39978             tag: 'input',
39979             id : id,
39980             cls : 'form-control roo-money-amount-input',
39981             autocomplete: 'new-password'
39982         };
39983         
39984         if (this.name) {
39985             input.name = this.name;
39986         }
39987
39988         if (this.disabled) {
39989             input.disabled = true;
39990         }
39991
39992         var clg = 12 - this.inputlg;
39993         var cmd = 12 - this.inputmd;
39994         var csm = 12 - this.inputsm;
39995         var cxs = 12 - this.inputxs;
39996         
39997         var container = {
39998             tag : 'div',
39999             cls : 'row roo-money-field',
40000             cn : [
40001                 {
40002                     tag : 'div',
40003                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40004                     cn : [
40005                         {
40006                             tag : 'div',
40007                             cls: 'roo-select2-container input-group',
40008                             cn: [
40009                                 {
40010                                     tag : 'input',
40011                                     cls : 'form-control roo-money-currency-input',
40012                                     autocomplete: 'new-password',
40013                                     readOnly : 1,
40014                                     name : this.currencyName
40015                                 },
40016                                 {
40017                                     tag :'span',
40018                                     cls : 'input-group-addon',
40019                                     cn : [
40020                                         {
40021                                             tag: 'span',
40022                                             cls: 'caret'
40023                                         }
40024                                     ]
40025                                 }
40026                             ]
40027                         }
40028                     ]
40029                 },
40030                 {
40031                     tag : 'div',
40032                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40033                     cn : [
40034                         {
40035                             tag: 'div',
40036                             cls: this.hasFeedback ? 'has-feedback' : '',
40037                             cn: [
40038                                 input
40039                             ]
40040                         }
40041                     ]
40042                 }
40043             ]
40044             
40045         };
40046         
40047         if (this.fieldLabel.length) {
40048             var indicator = {
40049                 tag: 'i',
40050                 tooltip: 'This field is required'
40051             };
40052
40053             var label = {
40054                 tag: 'label',
40055                 'for':  id,
40056                 cls: 'control-label',
40057                 cn: []
40058             };
40059
40060             var label_text = {
40061                 tag: 'span',
40062                 html: this.fieldLabel
40063             };
40064
40065             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40066             label.cn = [
40067                 indicator,
40068                 label_text
40069             ];
40070
40071             if(this.indicatorpos == 'right') {
40072                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40073                 label.cn = [
40074                     label_text,
40075                     indicator
40076                 ];
40077             }
40078
40079             if(align == 'left') {
40080                 container = {
40081                     tag: 'div',
40082                     cn: [
40083                         container
40084                     ]
40085                 };
40086
40087                 if(this.labelWidth > 12){
40088                     label.style = "width: " + this.labelWidth + 'px';
40089                 }
40090                 if(this.labelWidth < 13 && this.labelmd == 0){
40091                     this.labelmd = this.labelWidth;
40092                 }
40093                 if(this.labellg > 0){
40094                     label.cls += ' col-lg-' + this.labellg;
40095                     input.cls += ' col-lg-' + (12 - this.labellg);
40096                 }
40097                 if(this.labelmd > 0){
40098                     label.cls += ' col-md-' + this.labelmd;
40099                     container.cls += ' col-md-' + (12 - this.labelmd);
40100                 }
40101                 if(this.labelsm > 0){
40102                     label.cls += ' col-sm-' + this.labelsm;
40103                     container.cls += ' col-sm-' + (12 - this.labelsm);
40104                 }
40105                 if(this.labelxs > 0){
40106                     label.cls += ' col-xs-' + this.labelxs;
40107                     container.cls += ' col-xs-' + (12 - this.labelxs);
40108                 }
40109             }
40110         }
40111
40112         cfg.cn = [
40113             label,
40114             container
40115         ];
40116
40117         var settings = this;
40118
40119         ['xs','sm','md','lg'].map(function(size){
40120             if (settings[size]) {
40121                 cfg.cls += ' col-' + size + '-' + settings[size];
40122             }
40123         });
40124         
40125         return cfg;
40126         
40127     },
40128     
40129     initEvents : function()
40130     {
40131         this.indicator = this.indicatorEl();
40132         
40133         this.initCurrencyEvent();
40134         
40135         this.initNumberEvent();
40136         
40137     },
40138     
40139     initCurrencyEvent : function()
40140     {
40141         if (!this.store) {
40142             throw "can not find store for combo";
40143         }
40144         
40145         this.store = Roo.factory(this.store, Roo.data);
40146         this.store.parent = this;
40147         
40148         this.createList();
40149         
40150         this.triggerEl = this.el.select('.input-group-addon', true).first();
40151         
40152         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40153         
40154         var _this = this;
40155         
40156         (function(){
40157             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40158             _this.list.setWidth(lw);
40159         }).defer(100);
40160         
40161         this.list.on('mouseover', this.onViewOver, this);
40162         this.list.on('mousemove', this.onViewMove, this);
40163         this.list.on('scroll', this.onViewScroll, this);
40164         
40165         if(!this.tpl){
40166             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40167         }
40168         
40169         this.view = new Roo.View(this.list, this.tpl, {
40170             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40171         });
40172         
40173         this.view.on('click', this.onViewClick, this);
40174         
40175         this.store.on('beforeload', this.onBeforeLoad, this);
40176         this.store.on('load', this.onLoad, this);
40177         this.store.on('loadexception', this.onLoadException, this);
40178         
40179         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40180             "up" : function(e){
40181                 this.inKeyMode = true;
40182                 this.selectPrev();
40183             },
40184
40185             "down" : function(e){
40186                 if(!this.isExpanded()){
40187                     this.onTriggerClick();
40188                 }else{
40189                     this.inKeyMode = true;
40190                     this.selectNext();
40191                 }
40192             },
40193
40194             "enter" : function(e){
40195                 this.collapse();
40196                 
40197                 if(this.fireEvent("specialkey", this, e)){
40198                     this.onViewClick(false);
40199                 }
40200                 
40201                 return true;
40202             },
40203
40204             "esc" : function(e){
40205                 this.collapse();
40206             },
40207
40208             "tab" : function(e){
40209                 this.collapse();
40210                 
40211                 if(this.fireEvent("specialkey", this, e)){
40212                     this.onViewClick(false);
40213                 }
40214                 
40215                 return true;
40216             },
40217
40218             scope : this,
40219
40220             doRelay : function(foo, bar, hname){
40221                 if(hname == 'down' || this.scope.isExpanded()){
40222                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40223                 }
40224                 return true;
40225             },
40226
40227             forceKeyDown: true
40228         });
40229         
40230         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40231         
40232     },
40233     
40234     initNumberEvent : function(e)
40235     {
40236         this.inputEl().on("keydown" , this.fireKey,  this);
40237         this.inputEl().on("focus", this.onFocus,  this);
40238         this.inputEl().on("blur", this.onBlur,  this);
40239         
40240         this.inputEl().relayEvent('keyup', this);
40241         
40242         if(this.indicator){
40243             this.indicator.addClass('invisible');
40244         }
40245  
40246         this.originalValue = this.getValue();
40247         
40248         if(this.validationEvent == 'keyup'){
40249             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40250             this.inputEl().on('keyup', this.filterValidation, this);
40251         }
40252         else if(this.validationEvent !== false){
40253             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40254         }
40255         
40256         if(this.selectOnFocus){
40257             this.on("focus", this.preFocus, this);
40258             
40259         }
40260         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40261             this.inputEl().on("keypress", this.filterKeys, this);
40262         } else {
40263             this.inputEl().relayEvent('keypress', this);
40264         }
40265         
40266         var allowed = "0123456789";
40267         
40268         if(this.allowDecimals){
40269             allowed += this.decimalSeparator;
40270         }
40271         
40272         if(this.allowNegative){
40273             allowed += "-";
40274         }
40275         
40276         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40277         
40278         var keyPress = function(e){
40279             
40280             var k = e.getKey();
40281             
40282             var c = e.getCharCode();
40283             
40284             if(
40285                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40286                     allowed.indexOf(String.fromCharCode(c)) === -1
40287             ){
40288                 e.stopEvent();
40289                 return;
40290             }
40291             
40292             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40293                 return;
40294             }
40295             
40296             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40297                 e.stopEvent();
40298             }
40299         };
40300         
40301         this.inputEl().on("keypress", keyPress, this);
40302         
40303     },
40304     
40305     onTriggerClick : function(e)
40306     {   
40307         if(this.disabled){
40308             return;
40309         }
40310         
40311         this.page = 0;
40312         this.loadNext = false;
40313         
40314         if(this.isExpanded()){
40315             this.collapse();
40316             return;
40317         }
40318         
40319         this.hasFocus = true;
40320         
40321         if(this.triggerAction == 'all') {
40322             this.doQuery(this.allQuery, true);
40323             return;
40324         }
40325         
40326         this.doQuery(this.getRawValue());
40327     },
40328     
40329     getCurrency : function()
40330     {   
40331         var v = this.currencyEl().getValue();
40332         
40333         return v;
40334     },
40335     
40336     restrictHeight : function()
40337     {
40338         this.list.alignTo(this.currencyEl(), this.listAlign);
40339         this.list.alignTo(this.currencyEl(), this.listAlign);
40340     },
40341     
40342     onViewClick : function(view, doFocus, el, e)
40343     {
40344         var index = this.view.getSelectedIndexes()[0];
40345         
40346         var r = this.store.getAt(index);
40347         
40348         if(r){
40349             this.onSelect(r, index);
40350         }
40351     },
40352     
40353     onSelect : function(record, index){
40354         
40355         if(this.fireEvent('beforeselect', this, record, index) !== false){
40356         
40357             this.setFromCurrencyData(index > -1 ? record.data : false);
40358             
40359             this.collapse();
40360             
40361             this.fireEvent('select', this, record, index);
40362         }
40363     },
40364     
40365     setFromCurrencyData : function(o)
40366     {
40367         var currency = '';
40368         
40369         this.lastCurrency = o;
40370         
40371         if (this.currencyField) {
40372             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40373         } else {
40374             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40375         }
40376         
40377         this.lastSelectionText = currency;
40378         
40379         this.setCurrency(currency);
40380     },
40381     
40382     setFromData : function(o)
40383     {
40384         var c = {};
40385         
40386         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40387         
40388         this.setFromCurrencyData(c);
40389         
40390         var value = '';
40391         
40392         if (this.name) {
40393             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40394         } else {
40395             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40396         }
40397         
40398         this.setValue(value);
40399         
40400     },
40401     
40402     setCurrency : function(v)
40403     {   
40404         this.currencyValue = v;
40405         
40406         if(this.rendered){
40407             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40408             this.validate();
40409         }
40410     },
40411     
40412     setValue : function(v)
40413     {
40414         v = this.fixPrecision(v);
40415         
40416         v = String(v).replace(".", this.decimalSeparator);
40417         
40418         this.value = v;
40419         
40420         if(this.rendered){
40421             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40422             this.validate();
40423         }
40424     },
40425     
40426     getRawValue : function()
40427     {
40428         var v = this.inputEl().getValue();
40429         
40430         return v;
40431     },
40432     
40433     getValue : function()
40434     {
40435         return this.fixPrecision(this.parseValue(this.getRawValue()));
40436     },
40437     
40438     parseValue : function(value)
40439     {
40440         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40441         return isNaN(value) ? '' : value;
40442     },
40443     
40444     fixPrecision : function(value)
40445     {
40446         var nan = isNaN(value);
40447         
40448         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40449             return nan ? '' : value;
40450         }
40451         
40452         return parseFloat(value).toFixed(this.decimalPrecision);
40453     },
40454     
40455     decimalPrecisionFcn : function(v)
40456     {
40457         return Math.floor(v);
40458     },
40459     
40460     validateValue : function(value)
40461     {
40462         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40463             return false;
40464         }
40465         
40466         var num = this.parseValue(value);
40467         
40468         if(isNaN(num)){
40469             this.markInvalid(String.format(this.nanText, value));
40470             return false;
40471         }
40472         
40473         if(num < this.minValue){
40474             this.markInvalid(String.format(this.minText, this.minValue));
40475             return false;
40476         }
40477         
40478         if(num > this.maxValue){
40479             this.markInvalid(String.format(this.maxText, this.maxValue));
40480             return false;
40481         }
40482         
40483         return true;
40484     },
40485     
40486     validate : function()
40487     {
40488         if(this.disabled || this.allowBlank){
40489             this.markValid();
40490             return true;
40491         }
40492         
40493         var currency = this.getCurrency();
40494         
40495         if(this.validateValue(this.getRawValue()) && currency.length){
40496             this.markValid();
40497             return true;
40498         }
40499         
40500         this.markInvalid();
40501         return false;
40502     },
40503     
40504     getName: function()
40505     {
40506         return this.name;
40507     },
40508     
40509     beforeBlur : function()
40510     {
40511         if(!this.castInt){
40512             return;
40513         }
40514         
40515         var v = this.parseValue(this.getRawValue());
40516         
40517         if(v){
40518             this.setValue(v);
40519         }
40520     },
40521     
40522     onBlur : function()
40523     {
40524         this.beforeBlur();
40525         
40526         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40527             //this.el.removeClass(this.focusClass);
40528         }
40529         
40530         this.hasFocus = false;
40531         
40532         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40533             this.validate();
40534         }
40535         
40536         var v = this.getValue();
40537         
40538         if(String(v) !== String(this.startValue)){
40539             this.fireEvent('change', this, v, this.startValue);
40540         }
40541         
40542         this.fireEvent("blur", this);
40543     },
40544     
40545     inputEl : function()
40546     {
40547         return this.el.select('.roo-money-amount-input', true).first();
40548     },
40549     
40550     currencyEl : function()
40551     {
40552         return this.el.select('.roo-money-currency-input', true).first();
40553     }
40554     
40555 });