Roo/bootstrap/NumberField.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             // if parent was disabled, then do not try and create the children..
251             if(!this[cntr](true)){
252                 tree.items = [];
253                 return tree;
254             }
255            
256             cn = Roo.factory(tree);
257            
258             cn.parentType = this.xtype; //??
259             cn.parentId = this.id;
260             
261             var build_from_html =  Roo.XComponent.build_from_html;
262             
263             
264             // does the container contain child eleemnts with 'xtype' attributes.
265             // that match this xtype..
266             // note - when we render we create these as well..
267             // so we should check to see if body has xtype set.
268             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
269                
270                 var self_cntr_el = Roo.get(this[cntr](false));
271                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
272                 if (echild) { 
273                     //Roo.log(Roo.XComponent.build_from_html);
274                     //Roo.log("got echild:");
275                     //Roo.log(echild);
276                 }
277                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
278                 // and are not displayed -this causes this to use up the wrong element when matching.
279                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
280                 
281                 
282                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
283                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
284                   
285                   
286                   
287                     cn.el = echild;
288                   //  Roo.log("GOT");
289                     //echild.dom.removeAttribute('xtype');
290                 } else {
291                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
292                     Roo.debug && Roo.log(self_cntr_el);
293                     Roo.debug && Roo.log(echild);
294                     Roo.debug && Roo.log(cn);
295                 }
296             }
297            
298             
299            
300             // if object has flexy:if - then it may or may not be rendered.
301             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
302                 // skip a flexy if element.
303                 Roo.debug && Roo.log('skipping render');
304                 Roo.debug && Roo.log(tree);
305                 if (!cn.el) {
306                     Roo.debug && Roo.log('skipping all children');
307                     skip_children = true;
308                 }
309                 
310              } else {
311                  
312                 // actually if flexy:foreach is found, we really want to create 
313                 // multiple copies here...
314                 //Roo.log('render');
315                 //Roo.log(this[cntr]());
316                 // some elements do not have render methods.. like the layouts...
317                 /*
318                 if(this[cntr](true) === false){
319                     cn.items = [];
320                     return cn;
321                 }
322                 */
323                 cn.render && cn.render(this[cntr](true));
324                 
325              }
326             // then add the element..
327         }
328          
329         // handle the kids..
330         
331         var nitems = [];
332         /*
333         if (typeof (tree.menu) != 'undefined') {
334             tree.menu.parentType = cn.xtype;
335             tree.menu.triggerEl = cn.el;
336             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
337             
338         }
339         */
340         if (!tree.items || !tree.items.length) {
341             cn.items = nitems;
342             //Roo.log(["no children", this]);
343             
344             return cn;
345         }
346          
347         var items = tree.items;
348         delete tree.items;
349         
350         //Roo.log(items.length);
351             // add the items..
352         if (!skip_children) {    
353             for(var i =0;i < items.length;i++) {
354               //  Roo.log(['add child', items[i]]);
355                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
356             }
357         }
358         
359         cn.items = nitems;
360         
361         //Roo.log("fire childrenrendered");
362         
363         cn.fireEvent('childrenrendered', this);
364         
365         return cn;
366     },
367     /**
368      * Show a component - removes 'hidden' class
369      */
370     show : function()
371     {
372         if (this.el) {
373             this.el.removeClass('hidden');
374         }
375     },
376     /**
377      * Hide a component - adds 'hidden' class
378      */
379     hide: function()
380     {
381         if (this.el && !this.el.hasClass('hidden')) {
382             this.el.addClass('hidden');
383         }
384     }
385 });
386
387  /*
388  * - LGPL
389  *
390  * Body
391  *
392  */
393
394 /**
395  * @class Roo.bootstrap.Body
396  * @extends Roo.bootstrap.Component
397  * Bootstrap Body class
398  *
399  * @constructor
400  * Create a new body
401  * @param {Object} config The config object
402  */
403
404 Roo.bootstrap.Body = function(config){
405
406     config = config || {};
407
408     Roo.bootstrap.Body.superclass.constructor.call(this, config);
409     this.el = Roo.get(config.el ? config.el : document.body );
410     if (this.cls && this.cls.length) {
411         Roo.get(document.body).addClass(this.cls);
412     }
413 };
414
415 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
416
417     is_body : true,// just to make sure it's constructed?
418
419         autoCreate : {
420         cls: 'container'
421     },
422     onRender : function(ct, position)
423     {
424        /* Roo.log("Roo.bootstrap.Body - onRender");
425         if (this.cls && this.cls.length) {
426             Roo.get(document.body).addClass(this.cls);
427         }
428         // style??? xttr???
429         */
430     }
431
432
433
434
435 });
436 /*
437  * - LGPL
438  *
439  * button group
440  * 
441  */
442
443
444 /**
445  * @class Roo.bootstrap.ButtonGroup
446  * @extends Roo.bootstrap.Component
447  * Bootstrap ButtonGroup class
448  * @cfg {String} size lg | sm | xs (default empty normal)
449  * @cfg {String} align vertical | justified  (default none)
450  * @cfg {String} direction up | down (default down)
451  * @cfg {Boolean} toolbar false | true
452  * @cfg {Boolean} btn true | false
453  * 
454  * 
455  * @constructor
456  * Create a new Input
457  * @param {Object} config The config object
458  */
459
460 Roo.bootstrap.ButtonGroup = function(config){
461     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
462 };
463
464 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
465     
466     size: '',
467     align: '',
468     direction: '',
469     toolbar: false,
470     btn: true,
471
472     getAutoCreate : function(){
473         var cfg = {
474             cls: 'btn-group',
475             html : null
476         };
477         
478         cfg.html = this.html || cfg.html;
479         
480         if (this.toolbar) {
481             cfg = {
482                 cls: 'btn-toolbar',
483                 html: null
484             };
485             
486             return cfg;
487         }
488         
489         if (['vertical','justified'].indexOf(this.align)!==-1) {
490             cfg.cls = 'btn-group-' + this.align;
491             
492             if (this.align == 'justified') {
493                 console.log(this.items);
494             }
495         }
496         
497         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
498             cfg.cls += ' btn-group-' + this.size;
499         }
500         
501         if (this.direction == 'up') {
502             cfg.cls += ' dropup' ;
503         }
504         
505         return cfg;
506     }
507    
508 });
509
510  /*
511  * - LGPL
512  *
513  * button
514  * 
515  */
516
517 /**
518  * @class Roo.bootstrap.Button
519  * @extends Roo.bootstrap.Component
520  * Bootstrap Button class
521  * @cfg {String} html The button content
522  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
523  * @cfg {String} size ( lg | sm | xs)
524  * @cfg {String} tag ( a | input | submit)
525  * @cfg {String} href empty or href
526  * @cfg {Boolean} disabled default false;
527  * @cfg {Boolean} isClose default false;
528  * @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)
529  * @cfg {String} badge text for badge
530  * @cfg {String} theme default 
531  * @cfg {Boolean} inverse 
532  * @cfg {Boolean} toggle 
533  * @cfg {String} ontext text for on toggle state
534  * @cfg {String} offtext text for off toggle state
535  * @cfg {Boolean} defaulton 
536  * @cfg {Boolean} preventDefault  default true
537  * @cfg {Boolean} removeClass remove the standard class..
538  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
539  * 
540  * @constructor
541  * Create a new button
542  * @param {Object} config The config object
543  */
544
545
546 Roo.bootstrap.Button = function(config){
547     Roo.bootstrap.Button.superclass.constructor.call(this, config);
548     this.weightClass = ["btn-default", 
549                        "btn-primary", 
550                        "btn-success", 
551                        "btn-info", 
552                        "btn-warning",
553                        "btn-danger",
554                        "btn-link"
555                       ],  
556     this.addEvents({
557         // raw events
558         /**
559          * @event click
560          * When a butotn is pressed
561          * @param {Roo.bootstrap.Button} this
562          * @param {Roo.EventObject} e
563          */
564         "click" : true,
565          /**
566          * @event toggle
567          * After the button has been toggles
568          * @param {Roo.EventObject} e
569          * @param {boolean} pressed (also available as button.pressed)
570          */
571         "toggle" : true
572     });
573 };
574
575 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
576     html: false,
577     active: false,
578     weight: '',
579     size: '',
580     tag: 'button',
581     href: '',
582     disabled: false,
583     isClose: false,
584     glyphicon: '',
585     badge: '',
586     theme: 'default',
587     inverse: false,
588     
589     toggle: false,
590     ontext: 'ON',
591     offtext: 'OFF',
592     defaulton: true,
593     preventDefault: true,
594     removeClass: false,
595     name: false,
596     target: false,
597     
598     
599     pressed : null,
600      
601     
602     getAutoCreate : function(){
603         
604         var cfg = {
605             tag : 'button',
606             cls : 'roo-button',
607             html: ''
608         };
609         
610         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
611             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
612             this.tag = 'button';
613         } else {
614             cfg.tag = this.tag;
615         }
616         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
617         
618         if (this.toggle == true) {
619             cfg={
620                 tag: 'div',
621                 cls: 'slider-frame roo-button',
622                 cn: [
623                     {
624                         tag: 'span',
625                         'data-on-text':'ON',
626                         'data-off-text':'OFF',
627                         cls: 'slider-button',
628                         html: this.offtext
629                     }
630                 ]
631             };
632             
633             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634                 cfg.cls += ' '+this.weight;
635             }
636             
637             return cfg;
638         }
639         
640         if (this.isClose) {
641             cfg.cls += ' close';
642             
643             cfg["aria-hidden"] = true;
644             
645             cfg.html = "&times;";
646             
647             return cfg;
648         }
649         
650          
651         if (this.theme==='default') {
652             cfg.cls = 'btn roo-button';
653             
654             //if (this.parentType != 'Navbar') {
655             this.weight = this.weight.length ?  this.weight : 'default';
656             //}
657             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
658                 
659                 cfg.cls += ' btn-' + this.weight;
660             }
661         } else if (this.theme==='glow') {
662             
663             cfg.tag = 'a';
664             cfg.cls = 'btn-glow roo-button';
665             
666             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667                 
668                 cfg.cls += ' ' + this.weight;
669             }
670         }
671    
672         
673         if (this.inverse) {
674             this.cls += ' inverse';
675         }
676         
677         
678         if (this.active) {
679             cfg.cls += ' active';
680         }
681         
682         if (this.disabled) {
683             cfg.disabled = 'disabled';
684         }
685         
686         if (this.items) {
687             Roo.log('changing to ul' );
688             cfg.tag = 'ul';
689             this.glyphicon = 'caret';
690         }
691         
692         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
693          
694         //gsRoo.log(this.parentType);
695         if (this.parentType === 'Navbar' && !this.parent().bar) {
696             Roo.log('changing to li?');
697             
698             cfg.tag = 'li';
699             
700             cfg.cls = '';
701             cfg.cn =  [{
702                 tag : 'a',
703                 cls : 'roo-button',
704                 html : this.html,
705                 href : this.href || '#'
706             }];
707             if (this.menu) {
708                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
709                 cfg.cls += ' dropdown';
710             }   
711             
712             delete cfg.html;
713             
714         }
715         
716        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
717         
718         if (this.glyphicon) {
719             cfg.html = ' ' + cfg.html;
720             
721             cfg.cn = [
722                 {
723                     tag: 'span',
724                     cls: 'glyphicon glyphicon-' + this.glyphicon
725                 }
726             ];
727         }
728         
729         if (this.badge) {
730             cfg.html += ' ';
731             
732             cfg.tag = 'a';
733             
734 //            cfg.cls='btn roo-button';
735             
736             cfg.href=this.href;
737             
738             var value = cfg.html;
739             
740             if(this.glyphicon){
741                 value = {
742                             tag: 'span',
743                             cls: 'glyphicon glyphicon-' + this.glyphicon,
744                             html: this.html
745                         };
746                 
747             }
748             
749             cfg.cn = [
750                 value,
751                 {
752                     tag: 'span',
753                     cls: 'badge',
754                     html: this.badge
755                 }
756             ];
757             
758             cfg.html='';
759         }
760         
761         if (this.menu) {
762             cfg.cls += ' dropdown';
763             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
764         }
765         
766         if (cfg.tag !== 'a' && this.href !== '') {
767             throw "Tag must be a to set href.";
768         } else if (this.href.length > 0) {
769             cfg.href = this.href;
770         }
771         
772         if(this.removeClass){
773             cfg.cls = '';
774         }
775         
776         if(this.target){
777             cfg.target = this.target;
778         }
779         
780         return cfg;
781     },
782     initEvents: function() {
783        // Roo.log('init events?');
784 //        Roo.log(this.el.dom);
785         // add the menu...
786         
787         if (typeof (this.menu) != 'undefined') {
788             this.menu.parentType = this.xtype;
789             this.menu.triggerEl = this.el;
790             this.addxtype(Roo.apply({}, this.menu));
791         }
792
793
794        if (this.el.hasClass('roo-button')) {
795             this.el.on('click', this.onClick, this);
796        } else {
797             this.el.select('.roo-button').on('click', this.onClick, this);
798        }
799        
800        if(this.removeClass){
801            this.el.on('click', this.onClick, this);
802        }
803        
804        this.el.enableDisplayMode();
805         
806     },
807     onClick : function(e)
808     {
809         if (this.disabled) {
810             return;
811         }
812         
813         
814         Roo.log('button on click ');
815         if(this.preventDefault){
816             e.preventDefault();
817         }
818         if (this.pressed === true || this.pressed === false) {
819             this.pressed = !this.pressed;
820             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
821             this.fireEvent('toggle', this, e, this.pressed);
822         }
823         
824         
825         this.fireEvent('click', this, e);
826     },
827     
828     /**
829      * Enables this button
830      */
831     enable : function()
832     {
833         this.disabled = false;
834         this.el.removeClass('disabled');
835     },
836     
837     /**
838      * Disable this button
839      */
840     disable : function()
841     {
842         this.disabled = true;
843         this.el.addClass('disabled');
844     },
845      /**
846      * sets the active state on/off, 
847      * @param {Boolean} state (optional) Force a particular state
848      */
849     setActive : function(v) {
850         
851         this.el[v ? 'addClass' : 'removeClass']('active');
852     },
853      /**
854      * toggles the current active state 
855      */
856     toggleActive : function()
857     {
858        var active = this.el.hasClass('active');
859        this.setActive(!active);
860        
861         
862     },
863     setText : function(str)
864     {
865         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
866     },
867     getText : function()
868     {
869         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
870     },
871     hide: function() {
872        
873      
874         this.el.hide();   
875     },
876     show: function() {
877        
878         this.el.show();   
879     },
880     setWeight : function(str)
881     {
882           this.el.removeClass(this.weightClass);
883         this.el.addClass('btn-' + str);        
884     }
885     
886     
887 });
888
889  /*
890  * - LGPL
891  *
892  * column
893  * 
894  */
895
896 /**
897  * @class Roo.bootstrap.Column
898  * @extends Roo.bootstrap.Component
899  * Bootstrap Column class
900  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
901  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
902  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
903  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
904  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
905  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
906  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
907  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
908  *
909  * 
910  * @cfg {Boolean} hidden (true|false) hide the element
911  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
912  * @cfg {String} fa (ban|check|...) font awesome icon
913  * @cfg {Number} fasize (1|2|....) font awsome size
914
915  * @cfg {String} icon (info-sign|check|...) glyphicon name
916
917  * @cfg {String} html content of column.
918  * 
919  * @constructor
920  * Create a new Column
921  * @param {Object} config The config object
922  */
923
924 Roo.bootstrap.Column = function(config){
925     Roo.bootstrap.Column.superclass.constructor.call(this, config);
926 };
927
928 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
929     
930     xs: false,
931     sm: false,
932     md: false,
933     lg: false,
934     xsoff: false,
935     smoff: false,
936     mdoff: false,
937     lgoff: false,
938     html: '',
939     offset: 0,
940     alert: false,
941     fa: false,
942     icon : false,
943     hidden : false,
944     fasize : 1,
945     
946     getAutoCreate : function(){
947         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
948         
949         cfg = {
950             tag: 'div',
951             cls: 'column'
952         };
953         
954         var settings=this;
955         ['xs','sm','md','lg'].map(function(size){
956             //Roo.log( size + ':' + settings[size]);
957             
958             if (settings[size+'off'] !== false) {
959                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
960             }
961             
962             if (settings[size] === false) {
963                 return;
964             }
965             
966             if (!settings[size]) { // 0 = hidden
967                 cfg.cls += ' hidden-' + size;
968                 return;
969             }
970             cfg.cls += ' col-' + size + '-' + settings[size];
971             
972         });
973         
974         if (this.hidden) {
975             cfg.cls += ' hidden';
976         }
977         
978         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
979             cfg.cls +=' alert alert-' + this.alert;
980         }
981         
982         
983         if (this.html.length) {
984             cfg.html = this.html;
985         }
986         if (this.fa) {
987             var fasize = '';
988             if (this.fasize > 1) {
989                 fasize = ' fa-' + this.fasize + 'x';
990             }
991             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
992             
993             
994         }
995         if (this.icon) {
996             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
997         }
998         
999         return cfg;
1000     }
1001    
1002 });
1003
1004  
1005
1006  /*
1007  * - LGPL
1008  *
1009  * page container.
1010  * 
1011  */
1012
1013
1014 /**
1015  * @class Roo.bootstrap.Container
1016  * @extends Roo.bootstrap.Component
1017  * Bootstrap Container class
1018  * @cfg {Boolean} jumbotron is it a jumbotron element
1019  * @cfg {String} html content of element
1020  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1021  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1022  * @cfg {String} header content of header (for panel)
1023  * @cfg {String} footer content of footer (for panel)
1024  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1025  * @cfg {String} tag (header|aside|section) type of HTML tag.
1026  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1027  * @cfg {String} fa font awesome icon
1028  * @cfg {String} icon (info-sign|check|...) glyphicon name
1029  * @cfg {Boolean} hidden (true|false) hide the element
1030  * @cfg {Boolean} expandable (true|false) default false
1031  * @cfg {Boolean} expanded (true|false) default true
1032  * @cfg {String} rheader contet on the right of header
1033  * @cfg {Boolean} clickable (true|false) default false
1034
1035  *     
1036  * @constructor
1037  * Create a new Container
1038  * @param {Object} config The config object
1039  */
1040
1041 Roo.bootstrap.Container = function(config){
1042     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1043     
1044     this.addEvents({
1045         // raw events
1046          /**
1047          * @event expand
1048          * After the panel has been expand
1049          * 
1050          * @param {Roo.bootstrap.Container} this
1051          */
1052         "expand" : true,
1053         /**
1054          * @event collapse
1055          * After the panel has been collapsed
1056          * 
1057          * @param {Roo.bootstrap.Container} this
1058          */
1059         "collapse" : true,
1060         /**
1061          * @event click
1062          * When a element is chick
1063          * @param {Roo.bootstrap.Container} this
1064          * @param {Roo.EventObject} e
1065          */
1066         "click" : true
1067     });
1068 };
1069
1070 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1071     
1072     jumbotron : false,
1073     well: '',
1074     panel : '',
1075     header: '',
1076     footer : '',
1077     sticky: '',
1078     tag : false,
1079     alert : false,
1080     fa: false,
1081     icon : false,
1082     expandable : false,
1083     rheader : '',
1084     expanded : true,
1085     clickable: false,
1086   
1087      
1088     getChildContainer : function() {
1089         
1090         if(!this.el){
1091             return false;
1092         }
1093         
1094         if (this.panel.length) {
1095             return this.el.select('.panel-body',true).first();
1096         }
1097         
1098         return this.el;
1099     },
1100     
1101     
1102     getAutoCreate : function(){
1103         
1104         var cfg = {
1105             tag : this.tag || 'div',
1106             html : '',
1107             cls : ''
1108         };
1109         if (this.jumbotron) {
1110             cfg.cls = 'jumbotron';
1111         }
1112         
1113         
1114         
1115         // - this is applied by the parent..
1116         //if (this.cls) {
1117         //    cfg.cls = this.cls + '';
1118         //}
1119         
1120         if (this.sticky.length) {
1121             
1122             var bd = Roo.get(document.body);
1123             if (!bd.hasClass('bootstrap-sticky')) {
1124                 bd.addClass('bootstrap-sticky');
1125                 Roo.select('html',true).setStyle('height', '100%');
1126             }
1127              
1128             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1129         }
1130         
1131         
1132         if (this.well.length) {
1133             switch (this.well) {
1134                 case 'lg':
1135                 case 'sm':
1136                     cfg.cls +=' well well-' +this.well;
1137                     break;
1138                 default:
1139                     cfg.cls +=' well';
1140                     break;
1141             }
1142         }
1143         
1144         if (this.hidden) {
1145             cfg.cls += ' hidden';
1146         }
1147         
1148         
1149         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1150             cfg.cls +=' alert alert-' + this.alert;
1151         }
1152         
1153         var body = cfg;
1154         
1155         if (this.panel.length) {
1156             cfg.cls += ' panel panel-' + this.panel;
1157             cfg.cn = [];
1158             if (this.header.length) {
1159                 
1160                 var h = [];
1161                 
1162                 if(this.expandable){
1163                     
1164                     cfg.cls = cfg.cls + ' expandable';
1165                     
1166                     h.push({
1167                         tag: 'i',
1168                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1169                     });
1170                     
1171                 }
1172                 
1173                 h.push(
1174                     {
1175                         tag: 'span',
1176                         cls : 'panel-title',
1177                         html : (this.expandable ? '&nbsp;' : '') + this.header
1178                     },
1179                     {
1180                         tag: 'span',
1181                         cls: 'panel-header-right',
1182                         html: this.rheader
1183                     }
1184                 );
1185                 
1186                 cfg.cn.push({
1187                     cls : 'panel-heading',
1188                     style : this.expandable ? 'cursor: pointer' : '',
1189                     cn : h
1190                 });
1191                 
1192             }
1193             
1194             body = false;
1195             cfg.cn.push({
1196                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1197                 html : this.html
1198             });
1199             
1200             
1201             if (this.footer.length) {
1202                 cfg.cn.push({
1203                     cls : 'panel-footer',
1204                     html : this.footer
1205                     
1206                 });
1207             }
1208             
1209         }
1210         
1211         if (body) {
1212             body.html = this.html || cfg.html;
1213             // prefix with the icons..
1214             if (this.fa) {
1215                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1216             }
1217             if (this.icon) {
1218                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1219             }
1220             
1221             
1222         }
1223         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1224             cfg.cls =  'container';
1225         }
1226         
1227         return cfg;
1228     },
1229     
1230     initEvents: function() 
1231     {
1232         if(this.expandable){
1233             var headerEl = this.headerEl();
1234         
1235             if(headerEl){
1236                 headerEl.on('click', this.onToggleClick, this);
1237             }
1238         }
1239         
1240         if(this.clickable){
1241             this.el.on('click', this.onClick, this);
1242         }
1243         
1244     },
1245     
1246     onToggleClick : function()
1247     {
1248         var headerEl = this.headerEl();
1249         
1250         if(!headerEl){
1251             return;
1252         }
1253         
1254         if(this.expanded){
1255             this.collapse();
1256             return;
1257         }
1258         
1259         this.expand();
1260     },
1261     
1262     expand : function()
1263     {
1264         if(this.fireEvent('expand', this)) {
1265             
1266             this.expanded = true;
1267             
1268             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1269             
1270             this.el.select('.panel-body',true).first().removeClass('hide');
1271             
1272             var toggleEl = this.toggleEl();
1273
1274             if(!toggleEl){
1275                 return;
1276             }
1277
1278             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1279         }
1280         
1281     },
1282     
1283     collapse : function()
1284     {
1285         if(this.fireEvent('collapse', this)) {
1286             
1287             this.expanded = false;
1288             
1289             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1290             this.el.select('.panel-body',true).first().addClass('hide');
1291         
1292             var toggleEl = this.toggleEl();
1293
1294             if(!toggleEl){
1295                 return;
1296             }
1297
1298             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1299         }
1300     },
1301     
1302     toggleEl : function()
1303     {
1304         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1305             return;
1306         }
1307         
1308         return this.el.select('.panel-heading .fa',true).first();
1309     },
1310     
1311     headerEl : function()
1312     {
1313         if(!this.el || !this.panel.length || !this.header.length){
1314             return;
1315         }
1316         
1317         return this.el.select('.panel-heading',true).first()
1318     },
1319     
1320     bodyEl : function()
1321     {
1322         if(!this.el || !this.panel.length){
1323             return;
1324         }
1325         
1326         return this.el.select('.panel-body',true).first()
1327     },
1328     
1329     titleEl : function()
1330     {
1331         if(!this.el || !this.panel.length || !this.header.length){
1332             return;
1333         }
1334         
1335         return this.el.select('.panel-title',true).first();
1336     },
1337     
1338     setTitle : function(v)
1339     {
1340         var titleEl = this.titleEl();
1341         
1342         if(!titleEl){
1343             return;
1344         }
1345         
1346         titleEl.dom.innerHTML = v;
1347     },
1348     
1349     getTitle : function()
1350     {
1351         
1352         var titleEl = this.titleEl();
1353         
1354         if(!titleEl){
1355             return '';
1356         }
1357         
1358         return titleEl.dom.innerHTML;
1359     },
1360     
1361     setRightTitle : function(v)
1362     {
1363         var t = this.el.select('.panel-header-right',true).first();
1364         
1365         if(!t){
1366             return;
1367         }
1368         
1369         t.dom.innerHTML = v;
1370     },
1371     
1372     onClick : function(e)
1373     {
1374         e.preventDefault();
1375         
1376         this.fireEvent('click', this, e);
1377     },
1378     
1379     allChildren : function()
1380     {
1381         var r=new Roo.util.MixedCollection(false, function(o){
1382             return o.id || (o.id = Roo.id());
1383         });
1384         var iter = function(el) {
1385             if (el.inputEl) {
1386                 r.add(el);
1387             }
1388             if (!el.items) {
1389                 return;
1390             }
1391             Roo.each(el.items,function(e) {
1392                 iter(e);
1393             });
1394         };
1395
1396         iter(this);
1397         return r;
1398     },
1399     
1400     checkEmpty : function()
1401     {
1402         var items = this.allChildren();
1403         var isEmpty = true;
1404         
1405         items.each(function(f){
1406             if(f.el.isVisible()) {
1407                 isEmpty = false;
1408             }
1409         });
1410         
1411         return isEmpty;
1412     }
1413    
1414 });
1415
1416  /*
1417  * - LGPL
1418  *
1419  * image
1420  * 
1421  */
1422
1423
1424 /**
1425  * @class Roo.bootstrap.Img
1426  * @extends Roo.bootstrap.Component
1427  * Bootstrap Img class
1428  * @cfg {Boolean} imgResponsive false | true
1429  * @cfg {String} border rounded | circle | thumbnail
1430  * @cfg {String} src image source
1431  * @cfg {String} alt image alternative text
1432  * @cfg {String} href a tag href
1433  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1434  * @cfg {String} xsUrl xs image source
1435  * @cfg {String} smUrl sm image source
1436  * @cfg {String} mdUrl md image source
1437  * @cfg {String} lgUrl lg image source
1438  * 
1439  * @constructor
1440  * Create a new Input
1441  * @param {Object} config The config object
1442  */
1443
1444 Roo.bootstrap.Img = function(config){
1445     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1446     
1447     this.addEvents({
1448         // img events
1449         /**
1450          * @event click
1451          * The img click event for the img.
1452          * @param {Roo.EventObject} e
1453          */
1454         "click" : true
1455     });
1456 };
1457
1458 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1459     
1460     imgResponsive: true,
1461     border: '',
1462     src: 'about:blank',
1463     href: false,
1464     target: false,
1465     xsUrl: '',
1466     smUrl: '',
1467     mdUrl: '',
1468     lgUrl: '',
1469
1470     getAutoCreate : function()
1471     {   
1472         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1473             return this.createSingleImg();
1474         }
1475         
1476         var cfg = {
1477             tag: 'div',
1478             cls: 'roo-image-responsive-group',
1479             cn: []
1480         };
1481         var _this = this;
1482         
1483         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1484             
1485             if(!_this[size + 'Url']){
1486                 return;
1487             }
1488             
1489             var img = {
1490                 tag: 'img',
1491                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1492                 html: _this.html || cfg.html,
1493                 src: _this[size + 'Url']
1494             };
1495             
1496             img.cls += ' roo-image-responsive-' + size;
1497             
1498             var s = ['xs', 'sm', 'md', 'lg'];
1499             
1500             s.splice(s.indexOf(size), 1);
1501             
1502             Roo.each(s, function(ss){
1503                 img.cls += ' hidden-' + ss;
1504             });
1505             
1506             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1507                 cfg.cls += ' img-' + _this.border;
1508             }
1509             
1510             if(_this.alt){
1511                 cfg.alt = _this.alt;
1512             }
1513             
1514             if(_this.href){
1515                 var a = {
1516                     tag: 'a',
1517                     href: _this.href,
1518                     cn: [
1519                         img
1520                     ]
1521                 };
1522
1523                 if(this.target){
1524                     a.target = _this.target;
1525                 }
1526             }
1527             
1528             cfg.cn.push((_this.href) ? a : img);
1529             
1530         });
1531         
1532         return cfg;
1533     },
1534     
1535     createSingleImg : function()
1536     {
1537         var cfg = {
1538             tag: 'img',
1539             cls: (this.imgResponsive) ? 'img-responsive' : '',
1540             html : null,
1541             src : 'about:blank'  // just incase src get's set to undefined?!?
1542         };
1543         
1544         cfg.html = this.html || cfg.html;
1545         
1546         cfg.src = this.src || cfg.src;
1547         
1548         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1549             cfg.cls += ' img-' + this.border;
1550         }
1551         
1552         if(this.alt){
1553             cfg.alt = this.alt;
1554         }
1555         
1556         if(this.href){
1557             var a = {
1558                 tag: 'a',
1559                 href: this.href,
1560                 cn: [
1561                     cfg
1562                 ]
1563             };
1564             
1565             if(this.target){
1566                 a.target = this.target;
1567             }
1568             
1569         }
1570         
1571         return (this.href) ? a : cfg;
1572     },
1573     
1574     initEvents: function() 
1575     {
1576         if(!this.href){
1577             this.el.on('click', this.onClick, this);
1578         }
1579         
1580     },
1581     
1582     onClick : function(e)
1583     {
1584         Roo.log('img onclick');
1585         this.fireEvent('click', this, e);
1586     },
1587     /**
1588      * Sets the url of the image - used to update it
1589      * @param {String} url the url of the image
1590      */
1591     
1592     setSrc : function(url)
1593     {
1594         this.src =  url;
1595         
1596         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1597             this.el.dom.src =  url;
1598             return;
1599         }
1600         
1601         this.el.select('img', true).first().dom.src =  url;
1602     }
1603     
1604     
1605    
1606 });
1607
1608  /*
1609  * - LGPL
1610  *
1611  * image
1612  * 
1613  */
1614
1615
1616 /**
1617  * @class Roo.bootstrap.Link
1618  * @extends Roo.bootstrap.Component
1619  * Bootstrap Link Class
1620  * @cfg {String} alt image alternative text
1621  * @cfg {String} href a tag href
1622  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1623  * @cfg {String} html the content of the link.
1624  * @cfg {String} anchor name for the anchor link
1625  * @cfg {String} fa - favicon
1626
1627  * @cfg {Boolean} preventDefault (true | false) default false
1628
1629  * 
1630  * @constructor
1631  * Create a new Input
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Link = function(config){
1636     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // img events
1640         /**
1641          * @event click
1642          * The img click event for the img.
1643          * @param {Roo.EventObject} e
1644          */
1645         "click" : true
1646     });
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1650     
1651     href: false,
1652     target: false,
1653     preventDefault: false,
1654     anchor : false,
1655     alt : false,
1656     fa: false,
1657
1658
1659     getAutoCreate : function()
1660     {
1661         var html = this.html || '';
1662         
1663         if (this.fa !== false) {
1664             html = '<i class="fa fa-' + this.fa + '"></i>';
1665         }
1666         var cfg = {
1667             tag: 'a'
1668         };
1669         // anchor's do not require html/href...
1670         if (this.anchor === false) {
1671             cfg.html = html;
1672             cfg.href = this.href || '#';
1673         } else {
1674             cfg.name = this.anchor;
1675             if (this.html !== false || this.fa !== false) {
1676                 cfg.html = html;
1677             }
1678             if (this.href !== false) {
1679                 cfg.href = this.href;
1680             }
1681         }
1682         
1683         if(this.alt !== false){
1684             cfg.alt = this.alt;
1685         }
1686         
1687         
1688         if(this.target !== false) {
1689             cfg.target = this.target;
1690         }
1691         
1692         return cfg;
1693     },
1694     
1695     initEvents: function() {
1696         
1697         if(!this.href || this.preventDefault){
1698             this.el.on('click', this.onClick, this);
1699         }
1700     },
1701     
1702     onClick : function(e)
1703     {
1704         if(this.preventDefault){
1705             e.preventDefault();
1706         }
1707         //Roo.log('img onclick');
1708         this.fireEvent('click', this, e);
1709     }
1710    
1711 });
1712
1713  /*
1714  * - LGPL
1715  *
1716  * header
1717  * 
1718  */
1719
1720 /**
1721  * @class Roo.bootstrap.Header
1722  * @extends Roo.bootstrap.Component
1723  * Bootstrap Header class
1724  * @cfg {String} html content of header
1725  * @cfg {Number} level (1|2|3|4|5|6) default 1
1726  * 
1727  * @constructor
1728  * Create a new Header
1729  * @param {Object} config The config object
1730  */
1731
1732
1733 Roo.bootstrap.Header  = function(config){
1734     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1735 };
1736
1737 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1738     
1739     //href : false,
1740     html : false,
1741     level : 1,
1742     
1743     
1744     
1745     getAutoCreate : function(){
1746         
1747         
1748         
1749         var cfg = {
1750             tag: 'h' + (1 *this.level),
1751             html: this.html || ''
1752         } ;
1753         
1754         return cfg;
1755     }
1756    
1757 });
1758
1759  
1760
1761  /*
1762  * Based on:
1763  * Ext JS Library 1.1.1
1764  * Copyright(c) 2006-2007, Ext JS, LLC.
1765  *
1766  * Originally Released Under LGPL - original licence link has changed is not relivant.
1767  *
1768  * Fork - LGPL
1769  * <script type="text/javascript">
1770  */
1771  
1772 /**
1773  * @class Roo.bootstrap.MenuMgr
1774  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1775  * @singleton
1776  */
1777 Roo.bootstrap.MenuMgr = function(){
1778    var menus, active, groups = {}, attached = false, lastShow = new Date();
1779
1780    // private - called when first menu is created
1781    function init(){
1782        menus = {};
1783        active = new Roo.util.MixedCollection();
1784        Roo.get(document).addKeyListener(27, function(){
1785            if(active.length > 0){
1786                hideAll();
1787            }
1788        });
1789    }
1790
1791    // private
1792    function hideAll(){
1793        if(active && active.length > 0){
1794            var c = active.clone();
1795            c.each(function(m){
1796                m.hide();
1797            });
1798        }
1799    }
1800
1801    // private
1802    function onHide(m){
1803        active.remove(m);
1804        if(active.length < 1){
1805            Roo.get(document).un("mouseup", onMouseDown);
1806             
1807            attached = false;
1808        }
1809    }
1810
1811    // private
1812    function onShow(m){
1813        var last = active.last();
1814        lastShow = new Date();
1815        active.add(m);
1816        if(!attached){
1817           Roo.get(document).on("mouseup", onMouseDown);
1818            
1819            attached = true;
1820        }
1821        if(m.parentMenu){
1822           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1823           m.parentMenu.activeChild = m;
1824        }else if(last && last.isVisible()){
1825           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1826        }
1827    }
1828
1829    // private
1830    function onBeforeHide(m){
1831        if(m.activeChild){
1832            m.activeChild.hide();
1833        }
1834        if(m.autoHideTimer){
1835            clearTimeout(m.autoHideTimer);
1836            delete m.autoHideTimer;
1837        }
1838    }
1839
1840    // private
1841    function onBeforeShow(m){
1842        var pm = m.parentMenu;
1843        if(!pm && !m.allowOtherMenus){
1844            hideAll();
1845        }else if(pm && pm.activeChild && active != m){
1846            pm.activeChild.hide();
1847        }
1848    }
1849
1850    // private this should really trigger on mouseup..
1851    function onMouseDown(e){
1852         Roo.log("on Mouse Up");
1853         
1854         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1855             Roo.log("MenuManager hideAll");
1856             hideAll();
1857             e.stopEvent();
1858         }
1859         
1860         
1861    }
1862
1863    // private
1864    function onBeforeCheck(mi, state){
1865        if(state){
1866            var g = groups[mi.group];
1867            for(var i = 0, l = g.length; i < l; i++){
1868                if(g[i] != mi){
1869                    g[i].setChecked(false);
1870                }
1871            }
1872        }
1873    }
1874
1875    return {
1876
1877        /**
1878         * Hides all menus that are currently visible
1879         */
1880        hideAll : function(){
1881             hideAll();  
1882        },
1883
1884        // private
1885        register : function(menu){
1886            if(!menus){
1887                init();
1888            }
1889            menus[menu.id] = menu;
1890            menu.on("beforehide", onBeforeHide);
1891            menu.on("hide", onHide);
1892            menu.on("beforeshow", onBeforeShow);
1893            menu.on("show", onShow);
1894            var g = menu.group;
1895            if(g && menu.events["checkchange"]){
1896                if(!groups[g]){
1897                    groups[g] = [];
1898                }
1899                groups[g].push(menu);
1900                menu.on("checkchange", onCheck);
1901            }
1902        },
1903
1904         /**
1905          * Returns a {@link Roo.menu.Menu} object
1906          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1907          * be used to generate and return a new Menu instance.
1908          */
1909        get : function(menu){
1910            if(typeof menu == "string"){ // menu id
1911                return menus[menu];
1912            }else if(menu.events){  // menu instance
1913                return menu;
1914            }
1915            /*else if(typeof menu.length == 'number'){ // array of menu items?
1916                return new Roo.bootstrap.Menu({items:menu});
1917            }else{ // otherwise, must be a config
1918                return new Roo.bootstrap.Menu(menu);
1919            }
1920            */
1921            return false;
1922        },
1923
1924        // private
1925        unregister : function(menu){
1926            delete menus[menu.id];
1927            menu.un("beforehide", onBeforeHide);
1928            menu.un("hide", onHide);
1929            menu.un("beforeshow", onBeforeShow);
1930            menu.un("show", onShow);
1931            var g = menu.group;
1932            if(g && menu.events["checkchange"]){
1933                groups[g].remove(menu);
1934                menu.un("checkchange", onCheck);
1935            }
1936        },
1937
1938        // private
1939        registerCheckable : function(menuItem){
1940            var g = menuItem.group;
1941            if(g){
1942                if(!groups[g]){
1943                    groups[g] = [];
1944                }
1945                groups[g].push(menuItem);
1946                menuItem.on("beforecheckchange", onBeforeCheck);
1947            }
1948        },
1949
1950        // private
1951        unregisterCheckable : function(menuItem){
1952            var g = menuItem.group;
1953            if(g){
1954                groups[g].remove(menuItem);
1955                menuItem.un("beforecheckchange", onBeforeCheck);
1956            }
1957        }
1958    };
1959 }();/*
1960  * - LGPL
1961  *
1962  * menu
1963  * 
1964  */
1965
1966 /**
1967  * @class Roo.bootstrap.Menu
1968  * @extends Roo.bootstrap.Component
1969  * Bootstrap Menu class - container for MenuItems
1970  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1971  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1972  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1973  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1974  * 
1975  * @constructor
1976  * Create a new Menu
1977  * @param {Object} config The config object
1978  */
1979
1980
1981 Roo.bootstrap.Menu = function(config){
1982     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1983     if (this.registerMenu && this.type != 'treeview')  {
1984         Roo.bootstrap.MenuMgr.register(this);
1985     }
1986     this.addEvents({
1987         /**
1988          * @event beforeshow
1989          * Fires before this menu is displayed
1990          * @param {Roo.menu.Menu} this
1991          */
1992         beforeshow : true,
1993         /**
1994          * @event beforehide
1995          * Fires before this menu is hidden
1996          * @param {Roo.menu.Menu} this
1997          */
1998         beforehide : true,
1999         /**
2000          * @event show
2001          * Fires after this menu is displayed
2002          * @param {Roo.menu.Menu} this
2003          */
2004         show : true,
2005         /**
2006          * @event hide
2007          * Fires after this menu is hidden
2008          * @param {Roo.menu.Menu} this
2009          */
2010         hide : true,
2011         /**
2012          * @event click
2013          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2014          * @param {Roo.menu.Menu} this
2015          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2016          * @param {Roo.EventObject} e
2017          */
2018         click : true,
2019         /**
2020          * @event mouseover
2021          * Fires when the mouse is hovering over this menu
2022          * @param {Roo.menu.Menu} this
2023          * @param {Roo.EventObject} e
2024          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2025          */
2026         mouseover : true,
2027         /**
2028          * @event mouseout
2029          * Fires when the mouse exits this menu
2030          * @param {Roo.menu.Menu} this
2031          * @param {Roo.EventObject} e
2032          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2033          */
2034         mouseout : true,
2035         /**
2036          * @event itemclick
2037          * Fires when a menu item contained in this menu is clicked
2038          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2039          * @param {Roo.EventObject} e
2040          */
2041         itemclick: true
2042     });
2043     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2044 };
2045
2046 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2047     
2048    /// html : false,
2049     //align : '',
2050     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2051     type: false,
2052     /**
2053      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2054      */
2055     registerMenu : true,
2056     
2057     menuItems :false, // stores the menu items..
2058     
2059     hidden:true,
2060         
2061     parentMenu : false,
2062     
2063     stopEvent : true,
2064     
2065     isLink : false,
2066     
2067     getChildContainer : function() {
2068         return this.el;  
2069     },
2070     
2071     getAutoCreate : function(){
2072          
2073         //if (['right'].indexOf(this.align)!==-1) {
2074         //    cfg.cn[1].cls += ' pull-right'
2075         //}
2076         
2077         
2078         var cfg = {
2079             tag : 'ul',
2080             cls : 'dropdown-menu' ,
2081             style : 'z-index:1000'
2082             
2083         };
2084         
2085         if (this.type === 'submenu') {
2086             cfg.cls = 'submenu active';
2087         }
2088         if (this.type === 'treeview') {
2089             cfg.cls = 'treeview-menu';
2090         }
2091         
2092         return cfg;
2093     },
2094     initEvents : function() {
2095         
2096        // Roo.log("ADD event");
2097        // Roo.log(this.triggerEl.dom);
2098         
2099         this.triggerEl.on('click', this.onTriggerClick, this);
2100         
2101         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2102         
2103         this.triggerEl.addClass('dropdown-toggle');
2104         
2105         if (Roo.isTouch) {
2106             this.el.on('touchstart'  , this.onTouch, this);
2107         }
2108         this.el.on('click' , this.onClick, this);
2109
2110         this.el.on("mouseover", this.onMouseOver, this);
2111         this.el.on("mouseout", this.onMouseOut, this);
2112         
2113     },
2114     
2115     findTargetItem : function(e)
2116     {
2117         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2118         if(!t){
2119             return false;
2120         }
2121         //Roo.log(t);         Roo.log(t.id);
2122         if(t && t.id){
2123             //Roo.log(this.menuitems);
2124             return this.menuitems.get(t.id);
2125             
2126             //return this.items.get(t.menuItemId);
2127         }
2128         
2129         return false;
2130     },
2131     
2132     onTouch : function(e) 
2133     {
2134         Roo.log("menu.onTouch");
2135         //e.stopEvent(); this make the user popdown broken
2136         this.onClick(e);
2137     },
2138     
2139     onClick : function(e)
2140     {
2141         Roo.log("menu.onClick");
2142         
2143         var t = this.findTargetItem(e);
2144         if(!t || t.isContainer){
2145             return;
2146         }
2147         Roo.log(e);
2148         /*
2149         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2150             if(t == this.activeItem && t.shouldDeactivate(e)){
2151                 this.activeItem.deactivate();
2152                 delete this.activeItem;
2153                 return;
2154             }
2155             if(t.canActivate){
2156                 this.setActiveItem(t, true);
2157             }
2158             return;
2159             
2160             
2161         }
2162         */
2163        
2164         Roo.log('pass click event');
2165         
2166         t.onClick(e);
2167         
2168         this.fireEvent("click", this, t, e);
2169         
2170         var _this = this;
2171         
2172         if(!t.href.length || t.href == '#'){
2173             (function() { _this.hide(); }).defer(100);
2174         }
2175         
2176     },
2177     
2178     onMouseOver : function(e){
2179         var t  = this.findTargetItem(e);
2180         //Roo.log(t);
2181         //if(t){
2182         //    if(t.canActivate && !t.disabled){
2183         //        this.setActiveItem(t, true);
2184         //    }
2185         //}
2186         
2187         this.fireEvent("mouseover", this, e, t);
2188     },
2189     isVisible : function(){
2190         return !this.hidden;
2191     },
2192      onMouseOut : function(e){
2193         var t  = this.findTargetItem(e);
2194         
2195         //if(t ){
2196         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2197         //        this.activeItem.deactivate();
2198         //        delete this.activeItem;
2199         //    }
2200         //}
2201         this.fireEvent("mouseout", this, e, t);
2202     },
2203     
2204     
2205     /**
2206      * Displays this menu relative to another element
2207      * @param {String/HTMLElement/Roo.Element} element The element to align to
2208      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2209      * the element (defaults to this.defaultAlign)
2210      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2211      */
2212     show : function(el, pos, parentMenu){
2213         this.parentMenu = parentMenu;
2214         if(!this.el){
2215             this.render();
2216         }
2217         this.fireEvent("beforeshow", this);
2218         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2219     },
2220      /**
2221      * Displays this menu at a specific xy position
2222      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2223      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2224      */
2225     showAt : function(xy, parentMenu, /* private: */_e){
2226         this.parentMenu = parentMenu;
2227         if(!this.el){
2228             this.render();
2229         }
2230         if(_e !== false){
2231             this.fireEvent("beforeshow", this);
2232             //xy = this.el.adjustForConstraints(xy);
2233         }
2234         
2235         //this.el.show();
2236         this.hideMenuItems();
2237         this.hidden = false;
2238         this.triggerEl.addClass('open');
2239         
2240         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2241             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2242         }
2243         
2244         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2245             this.el.setXY(xy);
2246         }
2247         
2248         this.focus();
2249         this.fireEvent("show", this);
2250     },
2251     
2252     focus : function(){
2253         return;
2254         if(!this.hidden){
2255             this.doFocus.defer(50, this);
2256         }
2257     },
2258
2259     doFocus : function(){
2260         if(!this.hidden){
2261             this.focusEl.focus();
2262         }
2263     },
2264
2265     /**
2266      * Hides this menu and optionally all parent menus
2267      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2268      */
2269     hide : function(deep)
2270     {
2271         
2272         this.hideMenuItems();
2273         if(this.el && this.isVisible()){
2274             this.fireEvent("beforehide", this);
2275             if(this.activeItem){
2276                 this.activeItem.deactivate();
2277                 this.activeItem = null;
2278             }
2279             this.triggerEl.removeClass('open');;
2280             this.hidden = true;
2281             this.fireEvent("hide", this);
2282         }
2283         if(deep === true && this.parentMenu){
2284             this.parentMenu.hide(true);
2285         }
2286     },
2287     
2288     onTriggerClick : function(e)
2289     {
2290         Roo.log('trigger click');
2291         
2292         var target = e.getTarget();
2293         
2294         Roo.log(target.nodeName.toLowerCase());
2295         
2296         if(target.nodeName.toLowerCase() === 'i'){
2297             e.preventDefault();
2298         }
2299         
2300     },
2301     
2302     onTriggerPress  : function(e)
2303     {
2304         Roo.log('trigger press');
2305         //Roo.log(e.getTarget());
2306        // Roo.log(this.triggerEl.dom);
2307        
2308         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2309         var pel = Roo.get(e.getTarget());
2310         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2311             Roo.log('is treeview or dropdown?');
2312             return;
2313         }
2314         
2315         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2316             return;
2317         }
2318         
2319         if (this.isVisible()) {
2320             Roo.log('hide');
2321             this.hide();
2322         } else {
2323             Roo.log('show');
2324             this.show(this.triggerEl, false, false);
2325         }
2326         
2327         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2328             e.stopEvent();
2329         }
2330         
2331     },
2332        
2333     
2334     hideMenuItems : function()
2335     {
2336         Roo.log("hide Menu Items");
2337         if (!this.el) { 
2338             return;
2339         }
2340         //$(backdrop).remove()
2341         this.el.select('.open',true).each(function(aa) {
2342             
2343             aa.removeClass('open');
2344           //var parent = getParent($(this))
2345           //var relatedTarget = { relatedTarget: this }
2346           
2347            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2348           //if (e.isDefaultPrevented()) return
2349            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2350         });
2351     },
2352     addxtypeChild : function (tree, cntr) {
2353         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2354           
2355         this.menuitems.add(comp);
2356         return comp;
2357
2358     },
2359     getEl : function()
2360     {
2361         Roo.log(this.el);
2362         return this.el;
2363     }
2364 });
2365
2366  
2367  /*
2368  * - LGPL
2369  *
2370  * menu item
2371  * 
2372  */
2373
2374
2375 /**
2376  * @class Roo.bootstrap.MenuItem
2377  * @extends Roo.bootstrap.Component
2378  * Bootstrap MenuItem class
2379  * @cfg {String} html the menu label
2380  * @cfg {String} href the link
2381  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2382  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2383  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2384  * @cfg {String} fa favicon to show on left of menu item.
2385  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2386  * 
2387  * 
2388  * @constructor
2389  * Create a new MenuItem
2390  * @param {Object} config The config object
2391  */
2392
2393
2394 Roo.bootstrap.MenuItem = function(config){
2395     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2396     this.addEvents({
2397         // raw events
2398         /**
2399          * @event click
2400          * The raw click event for the entire grid.
2401          * @param {Roo.bootstrap.MenuItem} this
2402          * @param {Roo.EventObject} e
2403          */
2404         "click" : true
2405     });
2406 };
2407
2408 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2409     
2410     href : false,
2411     html : false,
2412     preventDefault: false,
2413     isContainer : false,
2414     active : false,
2415     fa: false,
2416     
2417     getAutoCreate : function(){
2418         
2419         if(this.isContainer){
2420             return {
2421                 tag: 'li',
2422                 cls: 'dropdown-menu-item'
2423             };
2424         }
2425         var ctag = {
2426             tag: 'span',
2427             html: 'Link'
2428         };
2429         
2430         var anc = {
2431             tag : 'a',
2432             href : '#',
2433             cn : [  ]
2434         };
2435         
2436         if (this.fa !== false) {
2437             anc.cn.push({
2438                 tag : 'i',
2439                 cls : 'fa fa-' + this.fa
2440             });
2441         }
2442         
2443         anc.cn.push(ctag);
2444         
2445         
2446         var cfg= {
2447             tag: 'li',
2448             cls: 'dropdown-menu-item',
2449             cn: [ anc ]
2450         };
2451         if (this.parent().type == 'treeview') {
2452             cfg.cls = 'treeview-menu';
2453         }
2454         if (this.active) {
2455             cfg.cls += ' active';
2456         }
2457         
2458         
2459         
2460         anc.href = this.href || cfg.cn[0].href ;
2461         ctag.html = this.html || cfg.cn[0].html ;
2462         return cfg;
2463     },
2464     
2465     initEvents: function()
2466     {
2467         if (this.parent().type == 'treeview') {
2468             this.el.select('a').on('click', this.onClick, this);
2469         }
2470         
2471         if (this.menu) {
2472             this.menu.parentType = this.xtype;
2473             this.menu.triggerEl = this.el;
2474             this.menu = this.addxtype(Roo.apply({}, this.menu));
2475         }
2476         
2477     },
2478     onClick : function(e)
2479     {
2480         Roo.log('item on click ');
2481         
2482         if(this.preventDefault){
2483             e.preventDefault();
2484         }
2485         //this.parent().hideMenuItems();
2486         
2487         this.fireEvent('click', this, e);
2488     },
2489     getEl : function()
2490     {
2491         return this.el;
2492     } 
2493 });
2494
2495  
2496
2497  /*
2498  * - LGPL
2499  *
2500  * menu separator
2501  * 
2502  */
2503
2504
2505 /**
2506  * @class Roo.bootstrap.MenuSeparator
2507  * @extends Roo.bootstrap.Component
2508  * Bootstrap MenuSeparator class
2509  * 
2510  * @constructor
2511  * Create a new MenuItem
2512  * @param {Object} config The config object
2513  */
2514
2515
2516 Roo.bootstrap.MenuSeparator = function(config){
2517     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2518 };
2519
2520 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2521     
2522     getAutoCreate : function(){
2523         var cfg = {
2524             cls: 'divider',
2525             tag : 'li'
2526         };
2527         
2528         return cfg;
2529     }
2530    
2531 });
2532
2533  
2534
2535  
2536 /*
2537 * Licence: LGPL
2538 */
2539
2540 /**
2541  * @class Roo.bootstrap.Modal
2542  * @extends Roo.bootstrap.Component
2543  * Bootstrap Modal class
2544  * @cfg {String} title Title of dialog
2545  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2546  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2547  * @cfg {Boolean} specificTitle default false
2548  * @cfg {Array} buttons Array of buttons or standard button set..
2549  * @cfg {String} buttonPosition (left|right|center) default right
2550  * @cfg {Boolean} animate default true
2551  * @cfg {Boolean} allow_close default true
2552  * @cfg {Boolean} fitwindow default false
2553  * @cfg {String} size (sm|lg) default empty
2554  *
2555  *
2556  * @constructor
2557  * Create a new Modal Dialog
2558  * @param {Object} config The config object
2559  */
2560
2561 Roo.bootstrap.Modal = function(config){
2562     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2563     this.addEvents({
2564         // raw events
2565         /**
2566          * @event btnclick
2567          * The raw btnclick event for the button
2568          * @param {Roo.EventObject} e
2569          */
2570         "btnclick" : true,
2571         /**
2572          * @event resize
2573          * Fire when dialog resize
2574          * @param {Roo.bootstrap.Modal} this
2575          * @param {Roo.EventObject} e
2576          */
2577         "resize" : true
2578     });
2579     this.buttons = this.buttons || [];
2580
2581     if (this.tmpl) {
2582         this.tmpl = Roo.factory(this.tmpl);
2583     }
2584
2585 };
2586
2587 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2588
2589     title : 'test dialog',
2590
2591     buttons : false,
2592
2593     // set on load...
2594
2595     html: false,
2596
2597     tmp: false,
2598
2599     specificTitle: false,
2600
2601     buttonPosition: 'right',
2602
2603     allow_close : true,
2604
2605     animate : true,
2606
2607     fitwindow: false,
2608
2609
2610      // private
2611     dialogEl: false,
2612     bodyEl:  false,
2613     footerEl:  false,
2614     titleEl:  false,
2615     closeEl:  false,
2616
2617     size: '',
2618
2619
2620     onRender : function(ct, position)
2621     {
2622         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2623
2624         if(!this.el){
2625             var cfg = Roo.apply({},  this.getAutoCreate());
2626             cfg.id = Roo.id();
2627             //if(!cfg.name){
2628             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2629             //}
2630             //if (!cfg.name.length) {
2631             //    delete cfg.name;
2632            // }
2633             if (this.cls) {
2634                 cfg.cls += ' ' + this.cls;
2635             }
2636             if (this.style) {
2637                 cfg.style = this.style;
2638             }
2639             this.el = Roo.get(document.body).createChild(cfg, position);
2640         }
2641         //var type = this.el.dom.type;
2642
2643
2644         if(this.tabIndex !== undefined){
2645             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2646         }
2647
2648         this.dialogEl = this.el.select('.modal-dialog',true).first();
2649         this.bodyEl = this.el.select('.modal-body',true).first();
2650         this.closeEl = this.el.select('.modal-header .close', true).first();
2651         this.headerEl = this.el.select('.modal-header',true).first();
2652         this.titleEl = this.el.select('.modal-title',true).first();
2653         this.footerEl = this.el.select('.modal-footer',true).first();
2654
2655         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2656         this.maskEl.enableDisplayMode("block");
2657         this.maskEl.hide();
2658         //this.el.addClass("x-dlg-modal");
2659
2660         if (this.buttons.length) {
2661             Roo.each(this.buttons, function(bb) {
2662                 var b = Roo.apply({}, bb);
2663                 b.xns = b.xns || Roo.bootstrap;
2664                 b.xtype = b.xtype || 'Button';
2665                 if (typeof(b.listeners) == 'undefined') {
2666                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2667                 }
2668
2669                 var btn = Roo.factory(b);
2670
2671                 btn.render(this.el.select('.modal-footer div').first());
2672
2673             },this);
2674         }
2675         // render the children.
2676         var nitems = [];
2677
2678         if(typeof(this.items) != 'undefined'){
2679             var items = this.items;
2680             delete this.items;
2681
2682             for(var i =0;i < items.length;i++) {
2683                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2684             }
2685         }
2686
2687         this.items = nitems;
2688
2689         // where are these used - they used to be body/close/footer
2690
2691
2692         this.initEvents();
2693         //this.el.addClass([this.fieldClass, this.cls]);
2694
2695     },
2696
2697     getAutoCreate : function(){
2698
2699
2700         var bdy = {
2701                 cls : 'modal-body',
2702                 html : this.html || ''
2703         };
2704
2705         var title = {
2706             tag: 'h4',
2707             cls : 'modal-title',
2708             html : this.title
2709         };
2710
2711         if(this.specificTitle){
2712             title = this.title;
2713
2714         };
2715
2716         var header = [];
2717         if (this.allow_close) {
2718             header.push({
2719                 tag: 'button',
2720                 cls : 'close',
2721                 html : '&times'
2722             });
2723         }
2724
2725         header.push(title);
2726
2727         var size = '';
2728
2729         if(this.size.length){
2730             size = 'modal-' + this.size;
2731         }
2732
2733         var modal = {
2734             cls: "modal",
2735             style : 'display: none',
2736             cn : [
2737                 {
2738                     cls: "modal-dialog " + size,
2739                     cn : [
2740                         {
2741                             cls : "modal-content",
2742                             cn : [
2743                                 {
2744                                     cls : 'modal-header',
2745                                     cn : header
2746                                 },
2747                                 bdy,
2748                                 {
2749                                     cls : 'modal-footer',
2750                                     cn : [
2751                                         {
2752                                             tag: 'div',
2753                                             cls: 'btn-' + this.buttonPosition
2754                                         }
2755                                     ]
2756
2757                                 }
2758
2759
2760                             ]
2761
2762                         }
2763                     ]
2764
2765                 }
2766             ]
2767         };
2768
2769         if(this.animate){
2770             modal.cls += ' fade';
2771         }
2772
2773         return modal;
2774
2775     },
2776     getChildContainer : function() {
2777
2778          return this.bodyEl;
2779
2780     },
2781     getButtonContainer : function() {
2782          return this.el.select('.modal-footer div',true).first();
2783
2784     },
2785     initEvents : function()
2786     {
2787         if (this.allow_close) {
2788             this.closeEl.on('click', this.hide, this);
2789         }
2790         Roo.EventManager.onWindowResize(this.resize, this, true);
2791
2792
2793     },
2794
2795     resize : function()
2796     {
2797         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2798         if (this.fitwindow) {
2799             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2800             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2801             this.setSize(w,h);
2802         }
2803     },
2804
2805     setSize : function(w,h)
2806     {
2807         if (!w && !h) {
2808             return;
2809         }
2810         this.resizeTo(w,h);
2811     },
2812
2813     show : function() {
2814
2815         if (!this.rendered) {
2816             this.render();
2817         }
2818
2819         this.el.setStyle('display', 'block');
2820
2821         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2822             var _this = this;
2823             (function(){
2824                 this.el.addClass('in');
2825             }).defer(50, this);
2826         }else{
2827             this.el.addClass('in');
2828
2829         }
2830
2831         // not sure how we can show data in here..
2832         //if (this.tmpl) {
2833         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2834         //}
2835
2836         Roo.get(document.body).addClass("x-body-masked");
2837         
2838         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2839         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2840         this.maskEl.show();
2841         
2842         this.resize();
2843         
2844         this.fireEvent('show', this);
2845
2846         // set zindex here - otherwise it appears to be ignored...
2847         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2848
2849         (function () {
2850             this.items.forEach( function(e) {
2851                 e.layout ? e.layout() : false;
2852
2853             });
2854         }).defer(100,this);
2855
2856     },
2857     hide : function()
2858     {
2859         if(this.fireEvent("beforehide", this) !== false){
2860             this.maskEl.hide();
2861             Roo.get(document.body).removeClass("x-body-masked");
2862             this.el.removeClass('in');
2863             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2864
2865             if(this.animate){ // why
2866                 var _this = this;
2867                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2868             }else{
2869                 this.el.setStyle('display', 'none');
2870             }
2871             this.fireEvent('hide', this);
2872         }
2873     },
2874
2875     addButton : function(str, cb)
2876     {
2877
2878
2879         var b = Roo.apply({}, { html : str } );
2880         b.xns = b.xns || Roo.bootstrap;
2881         b.xtype = b.xtype || 'Button';
2882         if (typeof(b.listeners) == 'undefined') {
2883             b.listeners = { click : cb.createDelegate(this)  };
2884         }
2885
2886         var btn = Roo.factory(b);
2887
2888         btn.render(this.el.select('.modal-footer div').first());
2889
2890         return btn;
2891
2892     },
2893
2894     setDefaultButton : function(btn)
2895     {
2896         //this.el.select('.modal-footer').()
2897     },
2898     diff : false,
2899
2900     resizeTo: function(w,h)
2901     {
2902         // skip.. ?? why??
2903
2904         this.dialogEl.setWidth(w);
2905         if (this.diff === false) {
2906             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2907         }
2908
2909         this.bodyEl.setHeight(h-this.diff);
2910
2911         this.fireEvent('resize', this);
2912
2913     },
2914     setContentSize  : function(w, h)
2915     {
2916
2917     },
2918     onButtonClick: function(btn,e)
2919     {
2920         //Roo.log([a,b,c]);
2921         this.fireEvent('btnclick', btn.name, e);
2922     },
2923      /**
2924      * Set the title of the Dialog
2925      * @param {String} str new Title
2926      */
2927     setTitle: function(str) {
2928         this.titleEl.dom.innerHTML = str;
2929     },
2930     /**
2931      * Set the body of the Dialog
2932      * @param {String} str new Title
2933      */
2934     setBody: function(str) {
2935         this.bodyEl.dom.innerHTML = str;
2936     },
2937     /**
2938      * Set the body of the Dialog using the template
2939      * @param {Obj} data - apply this data to the template and replace the body contents.
2940      */
2941     applyBody: function(obj)
2942     {
2943         if (!this.tmpl) {
2944             Roo.log("Error - using apply Body without a template");
2945             //code
2946         }
2947         this.tmpl.overwrite(this.bodyEl, obj);
2948     }
2949
2950 });
2951
2952
2953 Roo.apply(Roo.bootstrap.Modal,  {
2954     /**
2955          * Button config that displays a single OK button
2956          * @type Object
2957          */
2958         OK :  [{
2959             name : 'ok',
2960             weight : 'primary',
2961             html : 'OK'
2962         }],
2963         /**
2964          * Button config that displays Yes and No buttons
2965          * @type Object
2966          */
2967         YESNO : [
2968             {
2969                 name  : 'no',
2970                 html : 'No'
2971             },
2972             {
2973                 name  :'yes',
2974                 weight : 'primary',
2975                 html : 'Yes'
2976             }
2977         ],
2978
2979         /**
2980          * Button config that displays OK and Cancel buttons
2981          * @type Object
2982          */
2983         OKCANCEL : [
2984             {
2985                name : 'cancel',
2986                 html : 'Cancel'
2987             },
2988             {
2989                 name : 'ok',
2990                 weight : 'primary',
2991                 html : 'OK'
2992             }
2993         ],
2994         /**
2995          * Button config that displays Yes, No and Cancel buttons
2996          * @type Object
2997          */
2998         YESNOCANCEL : [
2999             {
3000                 name : 'yes',
3001                 weight : 'primary',
3002                 html : 'Yes'
3003             },
3004             {
3005                 name : 'no',
3006                 html : 'No'
3007             },
3008             {
3009                 name : 'cancel',
3010                 html : 'Cancel'
3011             }
3012         ],
3013         
3014         zIndex : 10001
3015 });
3016 /*
3017  * - LGPL
3018  *
3019  * messagebox - can be used as a replace
3020  * 
3021  */
3022 /**
3023  * @class Roo.MessageBox
3024  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3025  * Example usage:
3026  *<pre><code>
3027 // Basic alert:
3028 Roo.Msg.alert('Status', 'Changes saved successfully.');
3029
3030 // Prompt for user data:
3031 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3032     if (btn == 'ok'){
3033         // process text value...
3034     }
3035 });
3036
3037 // Show a dialog using config options:
3038 Roo.Msg.show({
3039    title:'Save Changes?',
3040    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3041    buttons: Roo.Msg.YESNOCANCEL,
3042    fn: processResult,
3043    animEl: 'elId'
3044 });
3045 </code></pre>
3046  * @singleton
3047  */
3048 Roo.bootstrap.MessageBox = function(){
3049     var dlg, opt, mask, waitTimer;
3050     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3051     var buttons, activeTextEl, bwidth;
3052
3053     
3054     // private
3055     var handleButton = function(button){
3056         dlg.hide();
3057         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3058     };
3059
3060     // private
3061     var handleHide = function(){
3062         if(opt && opt.cls){
3063             dlg.el.removeClass(opt.cls);
3064         }
3065         //if(waitTimer){
3066         //    Roo.TaskMgr.stop(waitTimer);
3067         //    waitTimer = null;
3068         //}
3069     };
3070
3071     // private
3072     var updateButtons = function(b){
3073         var width = 0;
3074         if(!b){
3075             buttons["ok"].hide();
3076             buttons["cancel"].hide();
3077             buttons["yes"].hide();
3078             buttons["no"].hide();
3079             //dlg.footer.dom.style.display = 'none';
3080             return width;
3081         }
3082         dlg.footerEl.dom.style.display = '';
3083         for(var k in buttons){
3084             if(typeof buttons[k] != "function"){
3085                 if(b[k]){
3086                     buttons[k].show();
3087                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3088                     width += buttons[k].el.getWidth()+15;
3089                 }else{
3090                     buttons[k].hide();
3091                 }
3092             }
3093         }
3094         return width;
3095     };
3096
3097     // private
3098     var handleEsc = function(d, k, e){
3099         if(opt && opt.closable !== false){
3100             dlg.hide();
3101         }
3102         if(e){
3103             e.stopEvent();
3104         }
3105     };
3106
3107     return {
3108         /**
3109          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3110          * @return {Roo.BasicDialog} The BasicDialog element
3111          */
3112         getDialog : function(){
3113            if(!dlg){
3114                 dlg = new Roo.bootstrap.Modal( {
3115                     //draggable: true,
3116                     //resizable:false,
3117                     //constraintoviewport:false,
3118                     //fixedcenter:true,
3119                     //collapsible : false,
3120                     //shim:true,
3121                     //modal: true,
3122                 //    width: 'auto',
3123                   //  height:100,
3124                     //buttonAlign:"center",
3125                     closeClick : function(){
3126                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3127                             handleButton("no");
3128                         }else{
3129                             handleButton("cancel");
3130                         }
3131                     }
3132                 });
3133                 dlg.render();
3134                 dlg.on("hide", handleHide);
3135                 mask = dlg.mask;
3136                 //dlg.addKeyListener(27, handleEsc);
3137                 buttons = {};
3138                 this.buttons = buttons;
3139                 var bt = this.buttonText;
3140                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3141                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3142                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3143                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3144                 //Roo.log(buttons);
3145                 bodyEl = dlg.bodyEl.createChild({
3146
3147                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3148                         '<textarea class="roo-mb-textarea"></textarea>' +
3149                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3150                 });
3151                 msgEl = bodyEl.dom.firstChild;
3152                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3153                 textboxEl.enableDisplayMode();
3154                 textboxEl.addKeyListener([10,13], function(){
3155                     if(dlg.isVisible() && opt && opt.buttons){
3156                         if(opt.buttons.ok){
3157                             handleButton("ok");
3158                         }else if(opt.buttons.yes){
3159                             handleButton("yes");
3160                         }
3161                     }
3162                 });
3163                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3164                 textareaEl.enableDisplayMode();
3165                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3166                 progressEl.enableDisplayMode();
3167                 
3168                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3169                 var pf = progressEl.dom.firstChild;
3170                 if (pf) {
3171                     pp = Roo.get(pf.firstChild);
3172                     pp.setHeight(pf.offsetHeight);
3173                 }
3174                 
3175             }
3176             return dlg;
3177         },
3178
3179         /**
3180          * Updates the message box body text
3181          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3182          * the XHTML-compliant non-breaking space character '&amp;#160;')
3183          * @return {Roo.MessageBox} This message box
3184          */
3185         updateText : function(text)
3186         {
3187             if(!dlg.isVisible() && !opt.width){
3188                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3189                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3190             }
3191             msgEl.innerHTML = text || '&#160;';
3192       
3193             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3194             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3195             var w = Math.max(
3196                     Math.min(opt.width || cw , this.maxWidth), 
3197                     Math.max(opt.minWidth || this.minWidth, bwidth)
3198             );
3199             if(opt.prompt){
3200                 activeTextEl.setWidth(w);
3201             }
3202             if(dlg.isVisible()){
3203                 dlg.fixedcenter = false;
3204             }
3205             // to big, make it scroll. = But as usual stupid IE does not support
3206             // !important..
3207             
3208             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3209                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3210                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3211             } else {
3212                 bodyEl.dom.style.height = '';
3213                 bodyEl.dom.style.overflowY = '';
3214             }
3215             if (cw > w) {
3216                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3217             } else {
3218                 bodyEl.dom.style.overflowX = '';
3219             }
3220             
3221             dlg.setContentSize(w, bodyEl.getHeight());
3222             if(dlg.isVisible()){
3223                 dlg.fixedcenter = true;
3224             }
3225             return this;
3226         },
3227
3228         /**
3229          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3230          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3231          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3232          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3233          * @return {Roo.MessageBox} This message box
3234          */
3235         updateProgress : function(value, text){
3236             if(text){
3237                 this.updateText(text);
3238             }
3239             
3240             if (pp) { // weird bug on my firefox - for some reason this is not defined
3241                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3242                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3243             }
3244             return this;
3245         },        
3246
3247         /**
3248          * Returns true if the message box is currently displayed
3249          * @return {Boolean} True if the message box is visible, else false
3250          */
3251         isVisible : function(){
3252             return dlg && dlg.isVisible();  
3253         },
3254
3255         /**
3256          * Hides the message box if it is displayed
3257          */
3258         hide : function(){
3259             if(this.isVisible()){
3260                 dlg.hide();
3261             }  
3262         },
3263
3264         /**
3265          * Displays a new message box, or reinitializes an existing message box, based on the config options
3266          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3267          * The following config object properties are supported:
3268          * <pre>
3269 Property    Type             Description
3270 ----------  ---------------  ------------------------------------------------------------------------------------
3271 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3272                                    closes (defaults to undefined)
3273 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3274                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3275 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3276                                    progress and wait dialogs will ignore this property and always hide the
3277                                    close button as they can only be closed programmatically.
3278 cls               String           A custom CSS class to apply to the message box element
3279 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3280                                    displayed (defaults to 75)
3281 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3282                                    function will be btn (the name of the button that was clicked, if applicable,
3283                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3284                                    Progress and wait dialogs will ignore this option since they do not respond to
3285                                    user actions and can only be closed programmatically, so any required function
3286                                    should be called by the same code after it closes the dialog.
3287 icon              String           A CSS class that provides a background image to be used as an icon for
3288                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3289 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3290 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3291 modal             Boolean          False to allow user interaction with the page while the message box is
3292                                    displayed (defaults to true)
3293 msg               String           A string that will replace the existing message box body text (defaults
3294                                    to the XHTML-compliant non-breaking space character '&#160;')
3295 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3296 progress          Boolean          True to display a progress bar (defaults to false)
3297 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3298 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3299 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3300 title             String           The title text
3301 value             String           The string value to set into the active textbox element if displayed
3302 wait              Boolean          True to display a progress bar (defaults to false)
3303 width             Number           The width of the dialog in pixels
3304 </pre>
3305          *
3306          * Example usage:
3307          * <pre><code>
3308 Roo.Msg.show({
3309    title: 'Address',
3310    msg: 'Please enter your address:',
3311    width: 300,
3312    buttons: Roo.MessageBox.OKCANCEL,
3313    multiline: true,
3314    fn: saveAddress,
3315    animEl: 'addAddressBtn'
3316 });
3317 </code></pre>
3318          * @param {Object} config Configuration options
3319          * @return {Roo.MessageBox} This message box
3320          */
3321         show : function(options)
3322         {
3323             
3324             // this causes nightmares if you show one dialog after another
3325             // especially on callbacks..
3326              
3327             if(this.isVisible()){
3328                 
3329                 this.hide();
3330                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3331                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3332                 Roo.log("New Dialog Message:" +  options.msg )
3333                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3334                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3335                 
3336             }
3337             var d = this.getDialog();
3338             opt = options;
3339             d.setTitle(opt.title || "&#160;");
3340             d.closeEl.setDisplayed(opt.closable !== false);
3341             activeTextEl = textboxEl;
3342             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3343             if(opt.prompt){
3344                 if(opt.multiline){
3345                     textboxEl.hide();
3346                     textareaEl.show();
3347                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3348                         opt.multiline : this.defaultTextHeight);
3349                     activeTextEl = textareaEl;
3350                 }else{
3351                     textboxEl.show();
3352                     textareaEl.hide();
3353                 }
3354             }else{
3355                 textboxEl.hide();
3356                 textareaEl.hide();
3357             }
3358             progressEl.setDisplayed(opt.progress === true);
3359             this.updateProgress(0);
3360             activeTextEl.dom.value = opt.value || "";
3361             if(opt.prompt){
3362                 dlg.setDefaultButton(activeTextEl);
3363             }else{
3364                 var bs = opt.buttons;
3365                 var db = null;
3366                 if(bs && bs.ok){
3367                     db = buttons["ok"];
3368                 }else if(bs && bs.yes){
3369                     db = buttons["yes"];
3370                 }
3371                 dlg.setDefaultButton(db);
3372             }
3373             bwidth = updateButtons(opt.buttons);
3374             this.updateText(opt.msg);
3375             if(opt.cls){
3376                 d.el.addClass(opt.cls);
3377             }
3378             d.proxyDrag = opt.proxyDrag === true;
3379             d.modal = opt.modal !== false;
3380             d.mask = opt.modal !== false ? mask : false;
3381             if(!d.isVisible()){
3382                 // force it to the end of the z-index stack so it gets a cursor in FF
3383                 document.body.appendChild(dlg.el.dom);
3384                 d.animateTarget = null;
3385                 d.show(options.animEl);
3386             }
3387             return this;
3388         },
3389
3390         /**
3391          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3392          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3393          * and closing the message box when the process is complete.
3394          * @param {String} title The title bar text
3395          * @param {String} msg The message box body text
3396          * @return {Roo.MessageBox} This message box
3397          */
3398         progress : function(title, msg){
3399             this.show({
3400                 title : title,
3401                 msg : msg,
3402                 buttons: false,
3403                 progress:true,
3404                 closable:false,
3405                 minWidth: this.minProgressWidth,
3406                 modal : true
3407             });
3408             return this;
3409         },
3410
3411         /**
3412          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3413          * If a callback function is passed it will be called after the user clicks the button, and the
3414          * id of the button that was clicked will be passed as the only parameter to the callback
3415          * (could also be the top-right close button).
3416          * @param {String} title The title bar text
3417          * @param {String} msg The message box body text
3418          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3419          * @param {Object} scope (optional) The scope of the callback function
3420          * @return {Roo.MessageBox} This message box
3421          */
3422         alert : function(title, msg, fn, scope)
3423         {
3424             this.show({
3425                 title : title,
3426                 msg : msg,
3427                 buttons: this.OK,
3428                 fn: fn,
3429                 closable : false,
3430                 scope : scope,
3431                 modal : true
3432             });
3433             return this;
3434         },
3435
3436         /**
3437          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3438          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3439          * You are responsible for closing the message box when the process is complete.
3440          * @param {String} msg The message box body text
3441          * @param {String} title (optional) The title bar text
3442          * @return {Roo.MessageBox} This message box
3443          */
3444         wait : function(msg, title){
3445             this.show({
3446                 title : title,
3447                 msg : msg,
3448                 buttons: false,
3449                 closable:false,
3450                 progress:true,
3451                 modal:true,
3452                 width:300,
3453                 wait:true
3454             });
3455             waitTimer = Roo.TaskMgr.start({
3456                 run: function(i){
3457                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3458                 },
3459                 interval: 1000
3460             });
3461             return this;
3462         },
3463
3464         /**
3465          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3466          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3467          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3468          * @param {String} title The title bar text
3469          * @param {String} msg The message box body text
3470          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3471          * @param {Object} scope (optional) The scope of the callback function
3472          * @return {Roo.MessageBox} This message box
3473          */
3474         confirm : function(title, msg, fn, scope){
3475             this.show({
3476                 title : title,
3477                 msg : msg,
3478                 buttons: this.YESNO,
3479                 fn: fn,
3480                 scope : scope,
3481                 modal : true
3482             });
3483             return this;
3484         },
3485
3486         /**
3487          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3488          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3489          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3490          * (could also be the top-right close button) and the text that was entered will be passed as the two
3491          * parameters to the callback.
3492          * @param {String} title The title bar text
3493          * @param {String} msg The message box body text
3494          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3495          * @param {Object} scope (optional) The scope of the callback function
3496          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3497          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3498          * @return {Roo.MessageBox} This message box
3499          */
3500         prompt : function(title, msg, fn, scope, multiline){
3501             this.show({
3502                 title : title,
3503                 msg : msg,
3504                 buttons: this.OKCANCEL,
3505                 fn: fn,
3506                 minWidth:250,
3507                 scope : scope,
3508                 prompt:true,
3509                 multiline: multiline,
3510                 modal : true
3511             });
3512             return this;
3513         },
3514
3515         /**
3516          * Button config that displays a single OK button
3517          * @type Object
3518          */
3519         OK : {ok:true},
3520         /**
3521          * Button config that displays Yes and No buttons
3522          * @type Object
3523          */
3524         YESNO : {yes:true, no:true},
3525         /**
3526          * Button config that displays OK and Cancel buttons
3527          * @type Object
3528          */
3529         OKCANCEL : {ok:true, cancel:true},
3530         /**
3531          * Button config that displays Yes, No and Cancel buttons
3532          * @type Object
3533          */
3534         YESNOCANCEL : {yes:true, no:true, cancel:true},
3535
3536         /**
3537          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3538          * @type Number
3539          */
3540         defaultTextHeight : 75,
3541         /**
3542          * The maximum width in pixels of the message box (defaults to 600)
3543          * @type Number
3544          */
3545         maxWidth : 600,
3546         /**
3547          * The minimum width in pixels of the message box (defaults to 100)
3548          * @type Number
3549          */
3550         minWidth : 100,
3551         /**
3552          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3553          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3554          * @type Number
3555          */
3556         minProgressWidth : 250,
3557         /**
3558          * An object containing the default button text strings that can be overriden for localized language support.
3559          * Supported properties are: ok, cancel, yes and no.
3560          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3561          * @type Object
3562          */
3563         buttonText : {
3564             ok : "OK",
3565             cancel : "Cancel",
3566             yes : "Yes",
3567             no : "No"
3568         }
3569     };
3570 }();
3571
3572 /**
3573  * Shorthand for {@link Roo.MessageBox}
3574  */
3575 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3576 Roo.Msg = Roo.Msg || Roo.MessageBox;
3577 /*
3578  * - LGPL
3579  *
3580  * navbar
3581  * 
3582  */
3583
3584 /**
3585  * @class Roo.bootstrap.Navbar
3586  * @extends Roo.bootstrap.Component
3587  * Bootstrap Navbar class
3588
3589  * @constructor
3590  * Create a new Navbar
3591  * @param {Object} config The config object
3592  */
3593
3594
3595 Roo.bootstrap.Navbar = function(config){
3596     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3597     this.addEvents({
3598         // raw events
3599         /**
3600          * @event beforetoggle
3601          * Fire before toggle the menu
3602          * @param {Roo.EventObject} e
3603          */
3604         "beforetoggle" : true
3605     });
3606 };
3607
3608 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3609     
3610     
3611    
3612     // private
3613     navItems : false,
3614     loadMask : false,
3615     
3616     
3617     getAutoCreate : function(){
3618         
3619         
3620         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3621         
3622     },
3623     
3624     initEvents :function ()
3625     {
3626         //Roo.log(this.el.select('.navbar-toggle',true));
3627         this.el.select('.navbar-toggle',true).on('click', function() {
3628             if(this.fireEvent('beforetoggle', this) !== false){
3629                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3630             }
3631             
3632         }, this);
3633         
3634         var mark = {
3635             tag: "div",
3636             cls:"x-dlg-mask"
3637         };
3638         
3639         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3640         
3641         var size = this.el.getSize();
3642         this.maskEl.setSize(size.width, size.height);
3643         this.maskEl.enableDisplayMode("block");
3644         this.maskEl.hide();
3645         
3646         if(this.loadMask){
3647             this.maskEl.show();
3648         }
3649     },
3650     
3651     
3652     getChildContainer : function()
3653     {
3654         if (this.el.select('.collapse').getCount()) {
3655             return this.el.select('.collapse',true).first();
3656         }
3657         
3658         return this.el;
3659     },
3660     
3661     mask : function()
3662     {
3663         this.maskEl.show();
3664     },
3665     
3666     unmask : function()
3667     {
3668         this.maskEl.hide();
3669     } 
3670     
3671     
3672     
3673     
3674 });
3675
3676
3677
3678  
3679
3680  /*
3681  * - LGPL
3682  *
3683  * navbar
3684  * 
3685  */
3686
3687 /**
3688  * @class Roo.bootstrap.NavSimplebar
3689  * @extends Roo.bootstrap.Navbar
3690  * Bootstrap Sidebar class
3691  *
3692  * @cfg {Boolean} inverse is inverted color
3693  * 
3694  * @cfg {String} type (nav | pills | tabs)
3695  * @cfg {Boolean} arrangement stacked | justified
3696  * @cfg {String} align (left | right) alignment
3697  * 
3698  * @cfg {Boolean} main (true|false) main nav bar? default false
3699  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3700  * 
3701  * @cfg {String} tag (header|footer|nav|div) default is nav 
3702
3703  * 
3704  * 
3705  * 
3706  * @constructor
3707  * Create a new Sidebar
3708  * @param {Object} config The config object
3709  */
3710
3711
3712 Roo.bootstrap.NavSimplebar = function(config){
3713     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3714 };
3715
3716 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3717     
3718     inverse: false,
3719     
3720     type: false,
3721     arrangement: '',
3722     align : false,
3723     
3724     
3725     
3726     main : false,
3727     
3728     
3729     tag : false,
3730     
3731     
3732     getAutoCreate : function(){
3733         
3734         
3735         var cfg = {
3736             tag : this.tag || 'div',
3737             cls : 'navbar'
3738         };
3739           
3740         
3741         cfg.cn = [
3742             {
3743                 cls: 'nav',
3744                 tag : 'ul'
3745             }
3746         ];
3747         
3748          
3749         this.type = this.type || 'nav';
3750         if (['tabs','pills'].indexOf(this.type)!==-1) {
3751             cfg.cn[0].cls += ' nav-' + this.type
3752         
3753         
3754         } else {
3755             if (this.type!=='nav') {
3756                 Roo.log('nav type must be nav/tabs/pills')
3757             }
3758             cfg.cn[0].cls += ' navbar-nav'
3759         }
3760         
3761         
3762         
3763         
3764         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3765             cfg.cn[0].cls += ' nav-' + this.arrangement;
3766         }
3767         
3768         
3769         if (this.align === 'right') {
3770             cfg.cn[0].cls += ' navbar-right';
3771         }
3772         
3773         if (this.inverse) {
3774             cfg.cls += ' navbar-inverse';
3775             
3776         }
3777         
3778         
3779         return cfg;
3780     
3781         
3782     }
3783     
3784     
3785     
3786 });
3787
3788
3789
3790  
3791
3792  
3793        /*
3794  * - LGPL
3795  *
3796  * navbar
3797  * 
3798  */
3799
3800 /**
3801  * @class Roo.bootstrap.NavHeaderbar
3802  * @extends Roo.bootstrap.NavSimplebar
3803  * Bootstrap Sidebar class
3804  *
3805  * @cfg {String} brand what is brand
3806  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3807  * @cfg {String} brand_href href of the brand
3808  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3809  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3810  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3811  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3812  * 
3813  * @constructor
3814  * Create a new Sidebar
3815  * @param {Object} config The config object
3816  */
3817
3818
3819 Roo.bootstrap.NavHeaderbar = function(config){
3820     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3821       
3822 };
3823
3824 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3825     
3826     position: '',
3827     brand: '',
3828     brand_href: false,
3829     srButton : true,
3830     autohide : false,
3831     desktopCenter : false,
3832    
3833     
3834     getAutoCreate : function(){
3835         
3836         var   cfg = {
3837             tag: this.nav || 'nav',
3838             cls: 'navbar',
3839             role: 'navigation',
3840             cn: []
3841         };
3842         
3843         var cn = cfg.cn;
3844         if (this.desktopCenter) {
3845             cn.push({cls : 'container', cn : []});
3846             cn = cn[0].cn;
3847         }
3848         
3849         if(this.srButton){
3850             cn.push({
3851                 tag: 'div',
3852                 cls: 'navbar-header',
3853                 cn: [
3854                     {
3855                         tag: 'button',
3856                         type: 'button',
3857                         cls: 'navbar-toggle',
3858                         'data-toggle': 'collapse',
3859                         cn: [
3860                             {
3861                                 tag: 'span',
3862                                 cls: 'sr-only',
3863                                 html: 'Toggle navigation'
3864                             },
3865                             {
3866                                 tag: 'span',
3867                                 cls: 'icon-bar'
3868                             },
3869                             {
3870                                 tag: 'span',
3871                                 cls: 'icon-bar'
3872                             },
3873                             {
3874                                 tag: 'span',
3875                                 cls: 'icon-bar'
3876                             }
3877                         ]
3878                     }
3879                 ]
3880             });
3881         }
3882         
3883         cn.push({
3884             tag: 'div',
3885             cls: 'collapse navbar-collapse',
3886             cn : []
3887         });
3888         
3889         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3890         
3891         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3892             cfg.cls += ' navbar-' + this.position;
3893             
3894             // tag can override this..
3895             
3896             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3897         }
3898         
3899         if (this.brand !== '') {
3900             cn[0].cn.push({
3901                 tag: 'a',
3902                 href: this.brand_href ? this.brand_href : '#',
3903                 cls: 'navbar-brand',
3904                 cn: [
3905                 this.brand
3906                 ]
3907             });
3908         }
3909         
3910         if(this.main){
3911             cfg.cls += ' main-nav';
3912         }
3913         
3914         
3915         return cfg;
3916
3917         
3918     },
3919     getHeaderChildContainer : function()
3920     {
3921         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3922             return this.el.select('.navbar-header',true).first();
3923         }
3924         
3925         return this.getChildContainer();
3926     },
3927     
3928     
3929     initEvents : function()
3930     {
3931         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3932         
3933         if (this.autohide) {
3934             
3935             var prevScroll = 0;
3936             var ft = this.el;
3937             
3938             Roo.get(document).on('scroll',function(e) {
3939                 var ns = Roo.get(document).getScroll().top;
3940                 var os = prevScroll;
3941                 prevScroll = ns;
3942                 
3943                 if(ns > os){
3944                     ft.removeClass('slideDown');
3945                     ft.addClass('slideUp');
3946                     return;
3947                 }
3948                 ft.removeClass('slideUp');
3949                 ft.addClass('slideDown');
3950                  
3951               
3952           },this);
3953         }
3954     }    
3955     
3956 });
3957
3958
3959
3960  
3961
3962  /*
3963  * - LGPL
3964  *
3965  * navbar
3966  * 
3967  */
3968
3969 /**
3970  * @class Roo.bootstrap.NavSidebar
3971  * @extends Roo.bootstrap.Navbar
3972  * Bootstrap Sidebar class
3973  * 
3974  * @constructor
3975  * Create a new Sidebar
3976  * @param {Object} config The config object
3977  */
3978
3979
3980 Roo.bootstrap.NavSidebar = function(config){
3981     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3982 };
3983
3984 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3985     
3986     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3987     
3988     getAutoCreate : function(){
3989         
3990         
3991         return  {
3992             tag: 'div',
3993             cls: 'sidebar sidebar-nav'
3994         };
3995     
3996         
3997     }
3998     
3999     
4000     
4001 });
4002
4003
4004
4005  
4006
4007  /*
4008  * - LGPL
4009  *
4010  * nav group
4011  * 
4012  */
4013
4014 /**
4015  * @class Roo.bootstrap.NavGroup
4016  * @extends Roo.bootstrap.Component
4017  * Bootstrap NavGroup class
4018  * @cfg {String} align (left|right)
4019  * @cfg {Boolean} inverse
4020  * @cfg {String} type (nav|pills|tab) default nav
4021  * @cfg {String} navId - reference Id for navbar.
4022
4023  * 
4024  * @constructor
4025  * Create a new nav group
4026  * @param {Object} config The config object
4027  */
4028
4029 Roo.bootstrap.NavGroup = function(config){
4030     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4031     this.navItems = [];
4032    
4033     Roo.bootstrap.NavGroup.register(this);
4034      this.addEvents({
4035         /**
4036              * @event changed
4037              * Fires when the active item changes
4038              * @param {Roo.bootstrap.NavGroup} this
4039              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4040              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4041          */
4042         'changed': true
4043      });
4044     
4045 };
4046
4047 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4048     
4049     align: '',
4050     inverse: false,
4051     form: false,
4052     type: 'nav',
4053     navId : '',
4054     // private
4055     
4056     navItems : false, 
4057     
4058     getAutoCreate : function()
4059     {
4060         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4061         
4062         cfg = {
4063             tag : 'ul',
4064             cls: 'nav' 
4065         };
4066         
4067         if (['tabs','pills'].indexOf(this.type)!==-1) {
4068             cfg.cls += ' nav-' + this.type
4069         } else {
4070             if (this.type!=='nav') {
4071                 Roo.log('nav type must be nav/tabs/pills')
4072             }
4073             cfg.cls += ' navbar-nav'
4074         }
4075         
4076         if (this.parent() && this.parent().sidebar) {
4077             cfg = {
4078                 tag: 'ul',
4079                 cls: 'dashboard-menu sidebar-menu'
4080             };
4081             
4082             return cfg;
4083         }
4084         
4085         if (this.form === true) {
4086             cfg = {
4087                 tag: 'form',
4088                 cls: 'navbar-form'
4089             };
4090             
4091             if (this.align === 'right') {
4092                 cfg.cls += ' navbar-right';
4093             } else {
4094                 cfg.cls += ' navbar-left';
4095             }
4096         }
4097         
4098         if (this.align === 'right') {
4099             cfg.cls += ' navbar-right';
4100         }
4101         
4102         if (this.inverse) {
4103             cfg.cls += ' navbar-inverse';
4104             
4105         }
4106         
4107         
4108         return cfg;
4109     },
4110     /**
4111     * sets the active Navigation item
4112     * @param {Roo.bootstrap.NavItem} the new current navitem
4113     */
4114     setActiveItem : function(item)
4115     {
4116         var prev = false;
4117         Roo.each(this.navItems, function(v){
4118             if (v == item) {
4119                 return ;
4120             }
4121             if (v.isActive()) {
4122                 v.setActive(false, true);
4123                 prev = v;
4124                 
4125             }
4126             
4127         });
4128
4129         item.setActive(true, true);
4130         this.fireEvent('changed', this, item, prev);
4131         
4132         
4133     },
4134     /**
4135     * gets the active Navigation item
4136     * @return {Roo.bootstrap.NavItem} the current navitem
4137     */
4138     getActive : function()
4139     {
4140         
4141         var prev = false;
4142         Roo.each(this.navItems, function(v){
4143             
4144             if (v.isActive()) {
4145                 prev = v;
4146                 
4147             }
4148             
4149         });
4150         return prev;
4151     },
4152     
4153     indexOfNav : function()
4154     {
4155         
4156         var prev = false;
4157         Roo.each(this.navItems, function(v,i){
4158             
4159             if (v.isActive()) {
4160                 prev = i;
4161                 
4162             }
4163             
4164         });
4165         return prev;
4166     },
4167     /**
4168     * adds a Navigation item
4169     * @param {Roo.bootstrap.NavItem} the navitem to add
4170     */
4171     addItem : function(cfg)
4172     {
4173         var cn = new Roo.bootstrap.NavItem(cfg);
4174         this.register(cn);
4175         cn.parentId = this.id;
4176         cn.onRender(this.el, null);
4177         return cn;
4178     },
4179     /**
4180     * register a Navigation item
4181     * @param {Roo.bootstrap.NavItem} the navitem to add
4182     */
4183     register : function(item)
4184     {
4185         this.navItems.push( item);
4186         item.navId = this.navId;
4187     
4188     },
4189     
4190     /**
4191     * clear all the Navigation item
4192     */
4193    
4194     clearAll : function()
4195     {
4196         this.navItems = [];
4197         this.el.dom.innerHTML = '';
4198     },
4199     
4200     getNavItem: function(tabId)
4201     {
4202         var ret = false;
4203         Roo.each(this.navItems, function(e) {
4204             if (e.tabId == tabId) {
4205                ret =  e;
4206                return false;
4207             }
4208             return true;
4209             
4210         });
4211         return ret;
4212     },
4213     
4214     setActiveNext : function()
4215     {
4216         var i = this.indexOfNav(this.getActive());
4217         if (i > this.navItems.length) {
4218             return;
4219         }
4220         this.setActiveItem(this.navItems[i+1]);
4221     },
4222     setActivePrev : function()
4223     {
4224         var i = this.indexOfNav(this.getActive());
4225         if (i  < 1) {
4226             return;
4227         }
4228         this.setActiveItem(this.navItems[i-1]);
4229     },
4230     clearWasActive : function(except) {
4231         Roo.each(this.navItems, function(e) {
4232             if (e.tabId != except.tabId && e.was_active) {
4233                e.was_active = false;
4234                return false;
4235             }
4236             return true;
4237             
4238         });
4239     },
4240     getWasActive : function ()
4241     {
4242         var r = false;
4243         Roo.each(this.navItems, function(e) {
4244             if (e.was_active) {
4245                r = e;
4246                return false;
4247             }
4248             return true;
4249             
4250         });
4251         return r;
4252     }
4253     
4254     
4255 });
4256
4257  
4258 Roo.apply(Roo.bootstrap.NavGroup, {
4259     
4260     groups: {},
4261      /**
4262     * register a Navigation Group
4263     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4264     */
4265     register : function(navgrp)
4266     {
4267         this.groups[navgrp.navId] = navgrp;
4268         
4269     },
4270     /**
4271     * fetch a Navigation Group based on the navigation ID
4272     * @param {string} the navgroup to add
4273     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4274     */
4275     get: function(navId) {
4276         if (typeof(this.groups[navId]) == 'undefined') {
4277             return false;
4278             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4279         }
4280         return this.groups[navId] ;
4281     }
4282     
4283     
4284     
4285 });
4286
4287  /*
4288  * - LGPL
4289  *
4290  * row
4291  * 
4292  */
4293
4294 /**
4295  * @class Roo.bootstrap.NavItem
4296  * @extends Roo.bootstrap.Component
4297  * Bootstrap Navbar.NavItem class
4298  * @cfg {String} href  link to
4299  * @cfg {String} html content of button
4300  * @cfg {String} badge text inside badge
4301  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4302  * @cfg {String} glyphicon name of glyphicon
4303  * @cfg {String} icon name of font awesome icon
4304  * @cfg {Boolean} active Is item active
4305  * @cfg {Boolean} disabled Is item disabled
4306  
4307  * @cfg {Boolean} preventDefault (true | false) default false
4308  * @cfg {String} tabId the tab that this item activates.
4309  * @cfg {String} tagtype (a|span) render as a href or span?
4310  * @cfg {Boolean} animateRef (true|false) link to element default false  
4311   
4312  * @constructor
4313  * Create a new Navbar Item
4314  * @param {Object} config The config object
4315  */
4316 Roo.bootstrap.NavItem = function(config){
4317     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4318     this.addEvents({
4319         // raw events
4320         /**
4321          * @event click
4322          * The raw click event for the entire grid.
4323          * @param {Roo.EventObject} e
4324          */
4325         "click" : true,
4326          /**
4327             * @event changed
4328             * Fires when the active item active state changes
4329             * @param {Roo.bootstrap.NavItem} this
4330             * @param {boolean} state the new state
4331              
4332          */
4333         'changed': true,
4334         /**
4335             * @event scrollto
4336             * Fires when scroll to element
4337             * @param {Roo.bootstrap.NavItem} this
4338             * @param {Object} options
4339             * @param {Roo.EventObject} e
4340              
4341          */
4342         'scrollto': true
4343     });
4344    
4345 };
4346
4347 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4348     
4349     href: false,
4350     html: '',
4351     badge: '',
4352     icon: false,
4353     glyphicon: false,
4354     active: false,
4355     preventDefault : false,
4356     tabId : false,
4357     tagtype : 'a',
4358     disabled : false,
4359     animateRef : false,
4360     was_active : false,
4361     
4362     getAutoCreate : function(){
4363          
4364         var cfg = {
4365             tag: 'li',
4366             cls: 'nav-item'
4367             
4368         };
4369         
4370         if (this.active) {
4371             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4372         }
4373         if (this.disabled) {
4374             cfg.cls += ' disabled';
4375         }
4376         
4377         if (this.href || this.html || this.glyphicon || this.icon) {
4378             cfg.cn = [
4379                 {
4380                     tag: this.tagtype,
4381                     href : this.href || "#",
4382                     html: this.html || ''
4383                 }
4384             ];
4385             
4386             if (this.icon) {
4387                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4388             }
4389
4390             if(this.glyphicon) {
4391                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4392             }
4393             
4394             if (this.menu) {
4395                 
4396                 cfg.cn[0].html += " <span class='caret'></span>";
4397              
4398             }
4399             
4400             if (this.badge !== '') {
4401                  
4402                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4403             }
4404         }
4405         
4406         
4407         
4408         return cfg;
4409     },
4410     initEvents: function() 
4411     {
4412         if (typeof (this.menu) != 'undefined') {
4413             this.menu.parentType = this.xtype;
4414             this.menu.triggerEl = this.el;
4415             this.menu = this.addxtype(Roo.apply({}, this.menu));
4416         }
4417         
4418         this.el.select('a',true).on('click', this.onClick, this);
4419         
4420         if(this.tagtype == 'span'){
4421             this.el.select('span',true).on('click', this.onClick, this);
4422         }
4423        
4424         // at this point parent should be available..
4425         this.parent().register(this);
4426     },
4427     
4428     onClick : function(e)
4429     {
4430         if (e.getTarget('.dropdown-menu-item')) {
4431             // did you click on a menu itemm.... - then don't trigger onclick..
4432             return;
4433         }
4434         
4435         if(
4436                 this.preventDefault || 
4437                 this.href == '#' 
4438         ){
4439             Roo.log("NavItem - prevent Default?");
4440             e.preventDefault();
4441         }
4442         
4443         if (this.disabled) {
4444             return;
4445         }
4446         
4447         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4448         if (tg && tg.transition) {
4449             Roo.log("waiting for the transitionend");
4450             return;
4451         }
4452         
4453         
4454         
4455         //Roo.log("fire event clicked");
4456         if(this.fireEvent('click', this, e) === false){
4457             return;
4458         };
4459         
4460         if(this.tagtype == 'span'){
4461             return;
4462         }
4463         
4464         //Roo.log(this.href);
4465         var ael = this.el.select('a',true).first();
4466         //Roo.log(ael);
4467         
4468         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4469             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4470             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4471                 return; // ignore... - it's a 'hash' to another page.
4472             }
4473             Roo.log("NavItem - prevent Default?");
4474             e.preventDefault();
4475             this.scrollToElement(e);
4476         }
4477         
4478         
4479         var p =  this.parent();
4480    
4481         if (['tabs','pills'].indexOf(p.type)!==-1) {
4482             if (typeof(p.setActiveItem) !== 'undefined') {
4483                 p.setActiveItem(this);
4484             }
4485         }
4486         
4487         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4488         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4489             // remove the collapsed menu expand...
4490             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4491         }
4492     },
4493     
4494     isActive: function () {
4495         return this.active
4496     },
4497     setActive : function(state, fire, is_was_active)
4498     {
4499         if (this.active && !state && this.navId) {
4500             this.was_active = true;
4501             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4502             if (nv) {
4503                 nv.clearWasActive(this);
4504             }
4505             
4506         }
4507         this.active = state;
4508         
4509         if (!state ) {
4510             this.el.removeClass('active');
4511         } else if (!this.el.hasClass('active')) {
4512             this.el.addClass('active');
4513         }
4514         if (fire) {
4515             this.fireEvent('changed', this, state);
4516         }
4517         
4518         // show a panel if it's registered and related..
4519         
4520         if (!this.navId || !this.tabId || !state || is_was_active) {
4521             return;
4522         }
4523         
4524         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4525         if (!tg) {
4526             return;
4527         }
4528         var pan = tg.getPanelByName(this.tabId);
4529         if (!pan) {
4530             return;
4531         }
4532         // if we can not flip to new panel - go back to old nav highlight..
4533         if (false == tg.showPanel(pan)) {
4534             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4535             if (nv) {
4536                 var onav = nv.getWasActive();
4537                 if (onav) {
4538                     onav.setActive(true, false, true);
4539                 }
4540             }
4541             
4542         }
4543         
4544         
4545         
4546     },
4547      // this should not be here...
4548     setDisabled : function(state)
4549     {
4550         this.disabled = state;
4551         if (!state ) {
4552             this.el.removeClass('disabled');
4553         } else if (!this.el.hasClass('disabled')) {
4554             this.el.addClass('disabled');
4555         }
4556         
4557     },
4558     
4559     /**
4560      * Fetch the element to display the tooltip on.
4561      * @return {Roo.Element} defaults to this.el
4562      */
4563     tooltipEl : function()
4564     {
4565         return this.el.select('' + this.tagtype + '', true).first();
4566     },
4567     
4568     scrollToElement : function(e)
4569     {
4570         var c = document.body;
4571         
4572         /*
4573          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4574          */
4575         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4576             c = document.documentElement;
4577         }
4578         
4579         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4580         
4581         if(!target){
4582             return;
4583         }
4584
4585         var o = target.calcOffsetsTo(c);
4586         
4587         var options = {
4588             target : target,
4589             value : o[1]
4590         };
4591         
4592         this.fireEvent('scrollto', this, options, e);
4593         
4594         Roo.get(c).scrollTo('top', options.value, true);
4595         
4596         return;
4597     }
4598 });
4599  
4600
4601  /*
4602  * - LGPL
4603  *
4604  * sidebar item
4605  *
4606  *  li
4607  *    <span> icon </span>
4608  *    <span> text </span>
4609  *    <span>badge </span>
4610  */
4611
4612 /**
4613  * @class Roo.bootstrap.NavSidebarItem
4614  * @extends Roo.bootstrap.NavItem
4615  * Bootstrap Navbar.NavSidebarItem class
4616  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4617  * {Boolean} open is the menu open
4618  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4619  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4620  * {String} buttonSize (sm|md|lg)the extra classes for the button
4621  * {Boolean} showArrow show arrow next to the text (default true)
4622  * @constructor
4623  * Create a new Navbar Button
4624  * @param {Object} config The config object
4625  */
4626 Roo.bootstrap.NavSidebarItem = function(config){
4627     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4628     this.addEvents({
4629         // raw events
4630         /**
4631          * @event click
4632          * The raw click event for the entire grid.
4633          * @param {Roo.EventObject} e
4634          */
4635         "click" : true,
4636          /**
4637             * @event changed
4638             * Fires when the active item active state changes
4639             * @param {Roo.bootstrap.NavSidebarItem} this
4640             * @param {boolean} state the new state
4641              
4642          */
4643         'changed': true
4644     });
4645    
4646 };
4647
4648 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4649     
4650     badgeWeight : 'default',
4651     
4652     open: false,
4653     
4654     buttonView : false,
4655     
4656     buttonWeight : 'default',
4657     
4658     buttonSize : 'md',
4659     
4660     showArrow : true,
4661     
4662     getAutoCreate : function(){
4663         
4664         
4665         var a = {
4666                 tag: 'a',
4667                 href : this.href || '#',
4668                 cls: '',
4669                 html : '',
4670                 cn : []
4671         };
4672         
4673         if(this.buttonView){
4674             a = {
4675                 tag: 'button',
4676                 href : this.href || '#',
4677                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4678                 html : this.html,
4679                 cn : []
4680             };
4681         }
4682         
4683         var cfg = {
4684             tag: 'li',
4685             cls: '',
4686             cn: [ a ]
4687         };
4688         
4689         if (this.active) {
4690             cfg.cls += ' active';
4691         }
4692         
4693         if (this.disabled) {
4694             cfg.cls += ' disabled';
4695         }
4696         if (this.open) {
4697             cfg.cls += ' open x-open';
4698         }
4699         // left icon..
4700         if (this.glyphicon || this.icon) {
4701             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4702             a.cn.push({ tag : 'i', cls : c }) ;
4703         }
4704         
4705         if(!this.buttonView){
4706             var span = {
4707                 tag: 'span',
4708                 html : this.html || ''
4709             };
4710
4711             a.cn.push(span);
4712             
4713         }
4714         
4715         if (this.badge !== '') {
4716             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4717         }
4718         
4719         if (this.menu) {
4720             
4721             if(this.showArrow){
4722                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4723             }
4724             
4725             a.cls += ' dropdown-toggle treeview' ;
4726         }
4727         
4728         return cfg;
4729     },
4730     
4731     initEvents : function()
4732     { 
4733         if (typeof (this.menu) != 'undefined') {
4734             this.menu.parentType = this.xtype;
4735             this.menu.triggerEl = this.el;
4736             this.menu = this.addxtype(Roo.apply({}, this.menu));
4737         }
4738         
4739         this.el.on('click', this.onClick, this);
4740         
4741         if(this.badge !== ''){
4742             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4743         }
4744         
4745     },
4746     
4747     onClick : function(e)
4748     {
4749         if(this.disabled){
4750             e.preventDefault();
4751             return;
4752         }
4753         
4754         if(this.preventDefault){
4755             e.preventDefault();
4756         }
4757         
4758         this.fireEvent('click', this);
4759     },
4760     
4761     disable : function()
4762     {
4763         this.setDisabled(true);
4764     },
4765     
4766     enable : function()
4767     {
4768         this.setDisabled(false);
4769     },
4770     
4771     setDisabled : function(state)
4772     {
4773         if(this.disabled == state){
4774             return;
4775         }
4776         
4777         this.disabled = state;
4778         
4779         if (state) {
4780             this.el.addClass('disabled');
4781             return;
4782         }
4783         
4784         this.el.removeClass('disabled');
4785         
4786         return;
4787     },
4788     
4789     setActive : function(state)
4790     {
4791         if(this.active == state){
4792             return;
4793         }
4794         
4795         this.active = state;
4796         
4797         if (state) {
4798             this.el.addClass('active');
4799             return;
4800         }
4801         
4802         this.el.removeClass('active');
4803         
4804         return;
4805     },
4806     
4807     isActive: function () 
4808     {
4809         return this.active;
4810     },
4811     
4812     setBadge : function(str)
4813     {
4814         if(!this.badgeEl){
4815             return;
4816         }
4817         
4818         this.badgeEl.dom.innerHTML = str;
4819     }
4820     
4821    
4822      
4823  
4824 });
4825  
4826
4827  /*
4828  * - LGPL
4829  *
4830  * row
4831  * 
4832  */
4833
4834 /**
4835  * @class Roo.bootstrap.Row
4836  * @extends Roo.bootstrap.Component
4837  * Bootstrap Row class (contains columns...)
4838  * 
4839  * @constructor
4840  * Create a new Row
4841  * @param {Object} config The config object
4842  */
4843
4844 Roo.bootstrap.Row = function(config){
4845     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4846 };
4847
4848 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4849     
4850     getAutoCreate : function(){
4851        return {
4852             cls: 'row clearfix'
4853        };
4854     }
4855     
4856     
4857 });
4858
4859  
4860
4861  /*
4862  * - LGPL
4863  *
4864  * element
4865  * 
4866  */
4867
4868 /**
4869  * @class Roo.bootstrap.Element
4870  * @extends Roo.bootstrap.Component
4871  * Bootstrap Element class
4872  * @cfg {String} html contents of the element
4873  * @cfg {String} tag tag of the element
4874  * @cfg {String} cls class of the element
4875  * @cfg {Boolean} preventDefault (true|false) default false
4876  * @cfg {Boolean} clickable (true|false) default false
4877  * 
4878  * @constructor
4879  * Create a new Element
4880  * @param {Object} config The config object
4881  */
4882
4883 Roo.bootstrap.Element = function(config){
4884     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4885     
4886     this.addEvents({
4887         // raw events
4888         /**
4889          * @event click
4890          * When a element is chick
4891          * @param {Roo.bootstrap.Element} this
4892          * @param {Roo.EventObject} e
4893          */
4894         "click" : true
4895     });
4896 };
4897
4898 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4899     
4900     tag: 'div',
4901     cls: '',
4902     html: '',
4903     preventDefault: false, 
4904     clickable: false,
4905     
4906     getAutoCreate : function(){
4907         
4908         var cfg = {
4909             tag: this.tag,
4910             cls: this.cls,
4911             html: this.html
4912         };
4913         
4914         return cfg;
4915     },
4916     
4917     initEvents: function() 
4918     {
4919         Roo.bootstrap.Element.superclass.initEvents.call(this);
4920         
4921         if(this.clickable){
4922             this.el.on('click', this.onClick, this);
4923         }
4924         
4925     },
4926     
4927     onClick : function(e)
4928     {
4929         if(this.preventDefault){
4930             e.preventDefault();
4931         }
4932         
4933         this.fireEvent('click', this, e);
4934     },
4935     
4936     getValue : function()
4937     {
4938         return this.el.dom.innerHTML;
4939     },
4940     
4941     setValue : function(value)
4942     {
4943         this.el.dom.innerHTML = value;
4944     }
4945    
4946 });
4947
4948  
4949
4950  /*
4951  * - LGPL
4952  *
4953  * pagination
4954  * 
4955  */
4956
4957 /**
4958  * @class Roo.bootstrap.Pagination
4959  * @extends Roo.bootstrap.Component
4960  * Bootstrap Pagination class
4961  * @cfg {String} size xs | sm | md | lg
4962  * @cfg {Boolean} inverse false | true
4963  * 
4964  * @constructor
4965  * Create a new Pagination
4966  * @param {Object} config The config object
4967  */
4968
4969 Roo.bootstrap.Pagination = function(config){
4970     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4971 };
4972
4973 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4974     
4975     cls: false,
4976     size: false,
4977     inverse: false,
4978     
4979     getAutoCreate : function(){
4980         var cfg = {
4981             tag: 'ul',
4982                 cls: 'pagination'
4983         };
4984         if (this.inverse) {
4985             cfg.cls += ' inverse';
4986         }
4987         if (this.html) {
4988             cfg.html=this.html;
4989         }
4990         if (this.cls) {
4991             cfg.cls += " " + this.cls;
4992         }
4993         return cfg;
4994     }
4995    
4996 });
4997
4998  
4999
5000  /*
5001  * - LGPL
5002  *
5003  * Pagination item
5004  * 
5005  */
5006
5007
5008 /**
5009  * @class Roo.bootstrap.PaginationItem
5010  * @extends Roo.bootstrap.Component
5011  * Bootstrap PaginationItem class
5012  * @cfg {String} html text
5013  * @cfg {String} href the link
5014  * @cfg {Boolean} preventDefault (true | false) default true
5015  * @cfg {Boolean} active (true | false) default false
5016  * @cfg {Boolean} disabled default false
5017  * 
5018  * 
5019  * @constructor
5020  * Create a new PaginationItem
5021  * @param {Object} config The config object
5022  */
5023
5024
5025 Roo.bootstrap.PaginationItem = function(config){
5026     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5027     this.addEvents({
5028         // raw events
5029         /**
5030          * @event click
5031          * The raw click event for the entire grid.
5032          * @param {Roo.EventObject} e
5033          */
5034         "click" : true
5035     });
5036 };
5037
5038 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5039     
5040     href : false,
5041     html : false,
5042     preventDefault: true,
5043     active : false,
5044     cls : false,
5045     disabled: false,
5046     
5047     getAutoCreate : function(){
5048         var cfg= {
5049             tag: 'li',
5050             cn: [
5051                 {
5052                     tag : 'a',
5053                     href : this.href ? this.href : '#',
5054                     html : this.html ? this.html : ''
5055                 }
5056             ]
5057         };
5058         
5059         if(this.cls){
5060             cfg.cls = this.cls;
5061         }
5062         
5063         if(this.disabled){
5064             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5065         }
5066         
5067         if(this.active){
5068             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5069         }
5070         
5071         return cfg;
5072     },
5073     
5074     initEvents: function() {
5075         
5076         this.el.on('click', this.onClick, this);
5077         
5078     },
5079     onClick : function(e)
5080     {
5081         Roo.log('PaginationItem on click ');
5082         if(this.preventDefault){
5083             e.preventDefault();
5084         }
5085         
5086         if(this.disabled){
5087             return;
5088         }
5089         
5090         this.fireEvent('click', this, e);
5091     }
5092    
5093 });
5094
5095  
5096
5097  /*
5098  * - LGPL
5099  *
5100  * slider
5101  * 
5102  */
5103
5104
5105 /**
5106  * @class Roo.bootstrap.Slider
5107  * @extends Roo.bootstrap.Component
5108  * Bootstrap Slider class
5109  *    
5110  * @constructor
5111  * Create a new Slider
5112  * @param {Object} config The config object
5113  */
5114
5115 Roo.bootstrap.Slider = function(config){
5116     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5117 };
5118
5119 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5120     
5121     getAutoCreate : function(){
5122         
5123         var cfg = {
5124             tag: 'div',
5125             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5126             cn: [
5127                 {
5128                     tag: 'a',
5129                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5130                 }
5131             ]
5132         };
5133         
5134         return cfg;
5135     }
5136    
5137 });
5138
5139  /*
5140  * Based on:
5141  * Ext JS Library 1.1.1
5142  * Copyright(c) 2006-2007, Ext JS, LLC.
5143  *
5144  * Originally Released Under LGPL - original licence link has changed is not relivant.
5145  *
5146  * Fork - LGPL
5147  * <script type="text/javascript">
5148  */
5149  
5150
5151 /**
5152  * @class Roo.grid.ColumnModel
5153  * @extends Roo.util.Observable
5154  * This is the default implementation of a ColumnModel used by the Grid. It defines
5155  * the columns in the grid.
5156  * <br>Usage:<br>
5157  <pre><code>
5158  var colModel = new Roo.grid.ColumnModel([
5159         {header: "Ticker", width: 60, sortable: true, locked: true},
5160         {header: "Company Name", width: 150, sortable: true},
5161         {header: "Market Cap.", width: 100, sortable: true},
5162         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5163         {header: "Employees", width: 100, sortable: true, resizable: false}
5164  ]);
5165  </code></pre>
5166  * <p>
5167  
5168  * The config options listed for this class are options which may appear in each
5169  * individual column definition.
5170  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5171  * @constructor
5172  * @param {Object} config An Array of column config objects. See this class's
5173  * config objects for details.
5174 */
5175 Roo.grid.ColumnModel = function(config){
5176         /**
5177      * The config passed into the constructor
5178      */
5179     this.config = config;
5180     this.lookup = {};
5181
5182     // if no id, create one
5183     // if the column does not have a dataIndex mapping,
5184     // map it to the order it is in the config
5185     for(var i = 0, len = config.length; i < len; i++){
5186         var c = config[i];
5187         if(typeof c.dataIndex == "undefined"){
5188             c.dataIndex = i;
5189         }
5190         if(typeof c.renderer == "string"){
5191             c.renderer = Roo.util.Format[c.renderer];
5192         }
5193         if(typeof c.id == "undefined"){
5194             c.id = Roo.id();
5195         }
5196         if(c.editor && c.editor.xtype){
5197             c.editor  = Roo.factory(c.editor, Roo.grid);
5198         }
5199         if(c.editor && c.editor.isFormField){
5200             c.editor = new Roo.grid.GridEditor(c.editor);
5201         }
5202         this.lookup[c.id] = c;
5203     }
5204
5205     /**
5206      * The width of columns which have no width specified (defaults to 100)
5207      * @type Number
5208      */
5209     this.defaultWidth = 100;
5210
5211     /**
5212      * Default sortable of columns which have no sortable specified (defaults to false)
5213      * @type Boolean
5214      */
5215     this.defaultSortable = false;
5216
5217     this.addEvents({
5218         /**
5219              * @event widthchange
5220              * Fires when the width of a column changes.
5221              * @param {ColumnModel} this
5222              * @param {Number} columnIndex The column index
5223              * @param {Number} newWidth The new width
5224              */
5225             "widthchange": true,
5226         /**
5227              * @event headerchange
5228              * Fires when the text of a header changes.
5229              * @param {ColumnModel} this
5230              * @param {Number} columnIndex The column index
5231              * @param {Number} newText The new header text
5232              */
5233             "headerchange": true,
5234         /**
5235              * @event hiddenchange
5236              * Fires when a column is hidden or "unhidden".
5237              * @param {ColumnModel} this
5238              * @param {Number} columnIndex The column index
5239              * @param {Boolean} hidden true if hidden, false otherwise
5240              */
5241             "hiddenchange": true,
5242             /**
5243          * @event columnmoved
5244          * Fires when a column is moved.
5245          * @param {ColumnModel} this
5246          * @param {Number} oldIndex
5247          * @param {Number} newIndex
5248          */
5249         "columnmoved" : true,
5250         /**
5251          * @event columlockchange
5252          * Fires when a column's locked state is changed
5253          * @param {ColumnModel} this
5254          * @param {Number} colIndex
5255          * @param {Boolean} locked true if locked
5256          */
5257         "columnlockchange" : true
5258     });
5259     Roo.grid.ColumnModel.superclass.constructor.call(this);
5260 };
5261 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5262     /**
5263      * @cfg {String} header The header text to display in the Grid view.
5264      */
5265     /**
5266      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5267      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5268      * specified, the column's index is used as an index into the Record's data Array.
5269      */
5270     /**
5271      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5272      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5273      */
5274     /**
5275      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5276      * Defaults to the value of the {@link #defaultSortable} property.
5277      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5278      */
5279     /**
5280      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5281      */
5282     /**
5283      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5284      */
5285     /**
5286      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5287      */
5288     /**
5289      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5290      */
5291     /**
5292      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5293      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5294      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5295      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5296      */
5297        /**
5298      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5299      */
5300     /**
5301      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5302      */
5303     /**
5304      * @cfg {String} cursor (Optional)
5305      */
5306     /**
5307      * @cfg {String} tooltip (Optional)
5308      */
5309     /**
5310      * @cfg {Number} xs (Optional)
5311      */
5312     /**
5313      * @cfg {Number} sm (Optional)
5314      */
5315     /**
5316      * @cfg {Number} md (Optional)
5317      */
5318     /**
5319      * @cfg {Number} lg (Optional)
5320      */
5321     /**
5322      * Returns the id of the column at the specified index.
5323      * @param {Number} index The column index
5324      * @return {String} the id
5325      */
5326     getColumnId : function(index){
5327         return this.config[index].id;
5328     },
5329
5330     /**
5331      * Returns the column for a specified id.
5332      * @param {String} id The column id
5333      * @return {Object} the column
5334      */
5335     getColumnById : function(id){
5336         return this.lookup[id];
5337     },
5338
5339     
5340     /**
5341      * Returns the column for a specified dataIndex.
5342      * @param {String} dataIndex The column dataIndex
5343      * @return {Object|Boolean} the column or false if not found
5344      */
5345     getColumnByDataIndex: function(dataIndex){
5346         var index = this.findColumnIndex(dataIndex);
5347         return index > -1 ? this.config[index] : false;
5348     },
5349     
5350     /**
5351      * Returns the index for a specified column id.
5352      * @param {String} id The column id
5353      * @return {Number} the index, or -1 if not found
5354      */
5355     getIndexById : function(id){
5356         for(var i = 0, len = this.config.length; i < len; i++){
5357             if(this.config[i].id == id){
5358                 return i;
5359             }
5360         }
5361         return -1;
5362     },
5363     
5364     /**
5365      * Returns the index for a specified column dataIndex.
5366      * @param {String} dataIndex The column dataIndex
5367      * @return {Number} the index, or -1 if not found
5368      */
5369     
5370     findColumnIndex : function(dataIndex){
5371         for(var i = 0, len = this.config.length; i < len; i++){
5372             if(this.config[i].dataIndex == dataIndex){
5373                 return i;
5374             }
5375         }
5376         return -1;
5377     },
5378     
5379     
5380     moveColumn : function(oldIndex, newIndex){
5381         var c = this.config[oldIndex];
5382         this.config.splice(oldIndex, 1);
5383         this.config.splice(newIndex, 0, c);
5384         this.dataMap = null;
5385         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5386     },
5387
5388     isLocked : function(colIndex){
5389         return this.config[colIndex].locked === true;
5390     },
5391
5392     setLocked : function(colIndex, value, suppressEvent){
5393         if(this.isLocked(colIndex) == value){
5394             return;
5395         }
5396         this.config[colIndex].locked = value;
5397         if(!suppressEvent){
5398             this.fireEvent("columnlockchange", this, colIndex, value);
5399         }
5400     },
5401
5402     getTotalLockedWidth : function(){
5403         var totalWidth = 0;
5404         for(var i = 0; i < this.config.length; i++){
5405             if(this.isLocked(i) && !this.isHidden(i)){
5406                 this.totalWidth += this.getColumnWidth(i);
5407             }
5408         }
5409         return totalWidth;
5410     },
5411
5412     getLockedCount : function(){
5413         for(var i = 0, len = this.config.length; i < len; i++){
5414             if(!this.isLocked(i)){
5415                 return i;
5416             }
5417         }
5418         
5419         return this.config.length;
5420     },
5421
5422     /**
5423      * Returns the number of columns.
5424      * @return {Number}
5425      */
5426     getColumnCount : function(visibleOnly){
5427         if(visibleOnly === true){
5428             var c = 0;
5429             for(var i = 0, len = this.config.length; i < len; i++){
5430                 if(!this.isHidden(i)){
5431                     c++;
5432                 }
5433             }
5434             return c;
5435         }
5436         return this.config.length;
5437     },
5438
5439     /**
5440      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5441      * @param {Function} fn
5442      * @param {Object} scope (optional)
5443      * @return {Array} result
5444      */
5445     getColumnsBy : function(fn, scope){
5446         var r = [];
5447         for(var i = 0, len = this.config.length; i < len; i++){
5448             var c = this.config[i];
5449             if(fn.call(scope||this, c, i) === true){
5450                 r[r.length] = c;
5451             }
5452         }
5453         return r;
5454     },
5455
5456     /**
5457      * Returns true if the specified column is sortable.
5458      * @param {Number} col The column index
5459      * @return {Boolean}
5460      */
5461     isSortable : function(col){
5462         if(typeof this.config[col].sortable == "undefined"){
5463             return this.defaultSortable;
5464         }
5465         return this.config[col].sortable;
5466     },
5467
5468     /**
5469      * Returns the rendering (formatting) function defined for the column.
5470      * @param {Number} col The column index.
5471      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5472      */
5473     getRenderer : function(col){
5474         if(!this.config[col].renderer){
5475             return Roo.grid.ColumnModel.defaultRenderer;
5476         }
5477         return this.config[col].renderer;
5478     },
5479
5480     /**
5481      * Sets the rendering (formatting) function for a column.
5482      * @param {Number} col The column index
5483      * @param {Function} fn The function to use to process the cell's raw data
5484      * to return HTML markup for the grid view. The render function is called with
5485      * the following parameters:<ul>
5486      * <li>Data value.</li>
5487      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5488      * <li>css A CSS style string to apply to the table cell.</li>
5489      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5490      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5491      * <li>Row index</li>
5492      * <li>Column index</li>
5493      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5494      */
5495     setRenderer : function(col, fn){
5496         this.config[col].renderer = fn;
5497     },
5498
5499     /**
5500      * Returns the width for the specified column.
5501      * @param {Number} col The column index
5502      * @return {Number}
5503      */
5504     getColumnWidth : function(col){
5505         return this.config[col].width * 1 || this.defaultWidth;
5506     },
5507
5508     /**
5509      * Sets the width for a column.
5510      * @param {Number} col The column index
5511      * @param {Number} width The new width
5512      */
5513     setColumnWidth : function(col, width, suppressEvent){
5514         this.config[col].width = width;
5515         this.totalWidth = null;
5516         if(!suppressEvent){
5517              this.fireEvent("widthchange", this, col, width);
5518         }
5519     },
5520
5521     /**
5522      * Returns the total width of all columns.
5523      * @param {Boolean} includeHidden True to include hidden column widths
5524      * @return {Number}
5525      */
5526     getTotalWidth : function(includeHidden){
5527         if(!this.totalWidth){
5528             this.totalWidth = 0;
5529             for(var i = 0, len = this.config.length; i < len; i++){
5530                 if(includeHidden || !this.isHidden(i)){
5531                     this.totalWidth += this.getColumnWidth(i);
5532                 }
5533             }
5534         }
5535         return this.totalWidth;
5536     },
5537
5538     /**
5539      * Returns the header for the specified column.
5540      * @param {Number} col The column index
5541      * @return {String}
5542      */
5543     getColumnHeader : function(col){
5544         return this.config[col].header;
5545     },
5546
5547     /**
5548      * Sets the header for a column.
5549      * @param {Number} col The column index
5550      * @param {String} header The new header
5551      */
5552     setColumnHeader : function(col, header){
5553         this.config[col].header = header;
5554         this.fireEvent("headerchange", this, col, header);
5555     },
5556
5557     /**
5558      * Returns the tooltip for the specified column.
5559      * @param {Number} col The column index
5560      * @return {String}
5561      */
5562     getColumnTooltip : function(col){
5563             return this.config[col].tooltip;
5564     },
5565     /**
5566      * Sets the tooltip for a column.
5567      * @param {Number} col The column index
5568      * @param {String} tooltip The new tooltip
5569      */
5570     setColumnTooltip : function(col, tooltip){
5571             this.config[col].tooltip = tooltip;
5572     },
5573
5574     /**
5575      * Returns the dataIndex for the specified column.
5576      * @param {Number} col The column index
5577      * @return {Number}
5578      */
5579     getDataIndex : function(col){
5580         return this.config[col].dataIndex;
5581     },
5582
5583     /**
5584      * Sets the dataIndex for a column.
5585      * @param {Number} col The column index
5586      * @param {Number} dataIndex The new dataIndex
5587      */
5588     setDataIndex : function(col, dataIndex){
5589         this.config[col].dataIndex = dataIndex;
5590     },
5591
5592     
5593     
5594     /**
5595      * Returns true if the cell is editable.
5596      * @param {Number} colIndex The column index
5597      * @param {Number} rowIndex The row index - this is nto actually used..?
5598      * @return {Boolean}
5599      */
5600     isCellEditable : function(colIndex, rowIndex){
5601         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5602     },
5603
5604     /**
5605      * Returns the editor defined for the cell/column.
5606      * return false or null to disable editing.
5607      * @param {Number} colIndex The column index
5608      * @param {Number} rowIndex The row index
5609      * @return {Object}
5610      */
5611     getCellEditor : function(colIndex, rowIndex){
5612         return this.config[colIndex].editor;
5613     },
5614
5615     /**
5616      * Sets if a column is editable.
5617      * @param {Number} col The column index
5618      * @param {Boolean} editable True if the column is editable
5619      */
5620     setEditable : function(col, editable){
5621         this.config[col].editable = editable;
5622     },
5623
5624
5625     /**
5626      * Returns true if the column is hidden.
5627      * @param {Number} colIndex The column index
5628      * @return {Boolean}
5629      */
5630     isHidden : function(colIndex){
5631         return this.config[colIndex].hidden;
5632     },
5633
5634
5635     /**
5636      * Returns true if the column width cannot be changed
5637      */
5638     isFixed : function(colIndex){
5639         return this.config[colIndex].fixed;
5640     },
5641
5642     /**
5643      * Returns true if the column can be resized
5644      * @return {Boolean}
5645      */
5646     isResizable : function(colIndex){
5647         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5648     },
5649     /**
5650      * Sets if a column is hidden.
5651      * @param {Number} colIndex The column index
5652      * @param {Boolean} hidden True if the column is hidden
5653      */
5654     setHidden : function(colIndex, hidden){
5655         this.config[colIndex].hidden = hidden;
5656         this.totalWidth = null;
5657         this.fireEvent("hiddenchange", this, colIndex, hidden);
5658     },
5659
5660     /**
5661      * Sets the editor for a column.
5662      * @param {Number} col The column index
5663      * @param {Object} editor The editor object
5664      */
5665     setEditor : function(col, editor){
5666         this.config[col].editor = editor;
5667     }
5668 });
5669
5670 Roo.grid.ColumnModel.defaultRenderer = function(value)
5671 {
5672     if(typeof value == "object") {
5673         return value;
5674     }
5675         if(typeof value == "string" && value.length < 1){
5676             return "&#160;";
5677         }
5678     
5679         return String.format("{0}", value);
5680 };
5681
5682 // Alias for backwards compatibility
5683 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5684 /*
5685  * Based on:
5686  * Ext JS Library 1.1.1
5687  * Copyright(c) 2006-2007, Ext JS, LLC.
5688  *
5689  * Originally Released Under LGPL - original licence link has changed is not relivant.
5690  *
5691  * Fork - LGPL
5692  * <script type="text/javascript">
5693  */
5694  
5695 /**
5696  * @class Roo.LoadMask
5697  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5698  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5699  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5700  * element's UpdateManager load indicator and will be destroyed after the initial load.
5701  * @constructor
5702  * Create a new LoadMask
5703  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5704  * @param {Object} config The config object
5705  */
5706 Roo.LoadMask = function(el, config){
5707     this.el = Roo.get(el);
5708     Roo.apply(this, config);
5709     if(this.store){
5710         this.store.on('beforeload', this.onBeforeLoad, this);
5711         this.store.on('load', this.onLoad, this);
5712         this.store.on('loadexception', this.onLoadException, this);
5713         this.removeMask = false;
5714     }else{
5715         var um = this.el.getUpdateManager();
5716         um.showLoadIndicator = false; // disable the default indicator
5717         um.on('beforeupdate', this.onBeforeLoad, this);
5718         um.on('update', this.onLoad, this);
5719         um.on('failure', this.onLoad, this);
5720         this.removeMask = true;
5721     }
5722 };
5723
5724 Roo.LoadMask.prototype = {
5725     /**
5726      * @cfg {Boolean} removeMask
5727      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5728      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5729      */
5730     /**
5731      * @cfg {String} msg
5732      * The text to display in a centered loading message box (defaults to 'Loading...')
5733      */
5734     msg : 'Loading...',
5735     /**
5736      * @cfg {String} msgCls
5737      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5738      */
5739     msgCls : 'x-mask-loading',
5740
5741     /**
5742      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5743      * @type Boolean
5744      */
5745     disabled: false,
5746
5747     /**
5748      * Disables the mask to prevent it from being displayed
5749      */
5750     disable : function(){
5751        this.disabled = true;
5752     },
5753
5754     /**
5755      * Enables the mask so that it can be displayed
5756      */
5757     enable : function(){
5758         this.disabled = false;
5759     },
5760     
5761     onLoadException : function()
5762     {
5763         Roo.log(arguments);
5764         
5765         if (typeof(arguments[3]) != 'undefined') {
5766             Roo.MessageBox.alert("Error loading",arguments[3]);
5767         } 
5768         /*
5769         try {
5770             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5771                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5772             }   
5773         } catch(e) {
5774             
5775         }
5776         */
5777     
5778         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5779     },
5780     // private
5781     onLoad : function()
5782     {
5783         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5784     },
5785
5786     // private
5787     onBeforeLoad : function(){
5788         if(!this.disabled){
5789             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5790         }
5791     },
5792
5793     // private
5794     destroy : function(){
5795         if(this.store){
5796             this.store.un('beforeload', this.onBeforeLoad, this);
5797             this.store.un('load', this.onLoad, this);
5798             this.store.un('loadexception', this.onLoadException, this);
5799         }else{
5800             var um = this.el.getUpdateManager();
5801             um.un('beforeupdate', this.onBeforeLoad, this);
5802             um.un('update', this.onLoad, this);
5803             um.un('failure', this.onLoad, this);
5804         }
5805     }
5806 };/*
5807  * - LGPL
5808  *
5809  * table
5810  * 
5811  */
5812
5813 /**
5814  * @class Roo.bootstrap.Table
5815  * @extends Roo.bootstrap.Component
5816  * Bootstrap Table class
5817  * @cfg {String} cls table class
5818  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5819  * @cfg {String} bgcolor Specifies the background color for a table
5820  * @cfg {Number} border Specifies whether the table cells should have borders or not
5821  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5822  * @cfg {Number} cellspacing Specifies the space between cells
5823  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5824  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5825  * @cfg {String} sortable Specifies that the table should be sortable
5826  * @cfg {String} summary Specifies a summary of the content of a table
5827  * @cfg {Number} width Specifies the width of a table
5828  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5829  * 
5830  * @cfg {boolean} striped Should the rows be alternative striped
5831  * @cfg {boolean} bordered Add borders to the table
5832  * @cfg {boolean} hover Add hover highlighting
5833  * @cfg {boolean} condensed Format condensed
5834  * @cfg {boolean} responsive Format condensed
5835  * @cfg {Boolean} loadMask (true|false) default false
5836  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5837  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5838  * @cfg {Boolean} rowSelection (true|false) default false
5839  * @cfg {Boolean} cellSelection (true|false) default false
5840  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5841  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5842  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5843  
5844  * 
5845  * @constructor
5846  * Create a new Table
5847  * @param {Object} config The config object
5848  */
5849
5850 Roo.bootstrap.Table = function(config){
5851     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5852     
5853   
5854     
5855     // BC...
5856     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5857     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5858     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5859     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5860     
5861     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5862     if (this.sm) {
5863         this.sm.grid = this;
5864         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5865         this.sm = this.selModel;
5866         this.sm.xmodule = this.xmodule || false;
5867     }
5868     
5869     if (this.cm && typeof(this.cm.config) == 'undefined') {
5870         this.colModel = new Roo.grid.ColumnModel(this.cm);
5871         this.cm = this.colModel;
5872         this.cm.xmodule = this.xmodule || false;
5873     }
5874     if (this.store) {
5875         this.store= Roo.factory(this.store, Roo.data);
5876         this.ds = this.store;
5877         this.ds.xmodule = this.xmodule || false;
5878          
5879     }
5880     if (this.footer && this.store) {
5881         this.footer.dataSource = this.ds;
5882         this.footer = Roo.factory(this.footer);
5883     }
5884     
5885     /** @private */
5886     this.addEvents({
5887         /**
5888          * @event cellclick
5889          * Fires when a cell is clicked
5890          * @param {Roo.bootstrap.Table} this
5891          * @param {Roo.Element} el
5892          * @param {Number} rowIndex
5893          * @param {Number} columnIndex
5894          * @param {Roo.EventObject} e
5895          */
5896         "cellclick" : true,
5897         /**
5898          * @event celldblclick
5899          * Fires when a cell is double clicked
5900          * @param {Roo.bootstrap.Table} this
5901          * @param {Roo.Element} el
5902          * @param {Number} rowIndex
5903          * @param {Number} columnIndex
5904          * @param {Roo.EventObject} e
5905          */
5906         "celldblclick" : true,
5907         /**
5908          * @event rowclick
5909          * Fires when a row is clicked
5910          * @param {Roo.bootstrap.Table} this
5911          * @param {Roo.Element} el
5912          * @param {Number} rowIndex
5913          * @param {Roo.EventObject} e
5914          */
5915         "rowclick" : true,
5916         /**
5917          * @event rowdblclick
5918          * Fires when a row is double clicked
5919          * @param {Roo.bootstrap.Table} this
5920          * @param {Roo.Element} el
5921          * @param {Number} rowIndex
5922          * @param {Roo.EventObject} e
5923          */
5924         "rowdblclick" : true,
5925         /**
5926          * @event mouseover
5927          * Fires when a mouseover occur
5928          * @param {Roo.bootstrap.Table} this
5929          * @param {Roo.Element} el
5930          * @param {Number} rowIndex
5931          * @param {Number} columnIndex
5932          * @param {Roo.EventObject} e
5933          */
5934         "mouseover" : true,
5935         /**
5936          * @event mouseout
5937          * Fires when a mouseout occur
5938          * @param {Roo.bootstrap.Table} this
5939          * @param {Roo.Element} el
5940          * @param {Number} rowIndex
5941          * @param {Number} columnIndex
5942          * @param {Roo.EventObject} e
5943          */
5944         "mouseout" : true,
5945         /**
5946          * @event rowclass
5947          * Fires when a row is rendered, so you can change add a style to it.
5948          * @param {Roo.bootstrap.Table} this
5949          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5950          */
5951         'rowclass' : true,
5952           /**
5953          * @event rowsrendered
5954          * Fires when all the  rows have been rendered
5955          * @param {Roo.bootstrap.Table} this
5956          */
5957         'rowsrendered' : true,
5958         /**
5959          * @event contextmenu
5960          * The raw contextmenu event for the entire grid.
5961          * @param {Roo.EventObject} e
5962          */
5963         "contextmenu" : true,
5964         /**
5965          * @event rowcontextmenu
5966          * Fires when a row is right clicked
5967          * @param {Roo.bootstrap.Table} this
5968          * @param {Number} rowIndex
5969          * @param {Roo.EventObject} e
5970          */
5971         "rowcontextmenu" : true,
5972         /**
5973          * @event cellcontextmenu
5974          * Fires when a cell is right clicked
5975          * @param {Roo.bootstrap.Table} this
5976          * @param {Number} rowIndex
5977          * @param {Number} cellIndex
5978          * @param {Roo.EventObject} e
5979          */
5980          "cellcontextmenu" : true,
5981          /**
5982          * @event headercontextmenu
5983          * Fires when a header is right clicked
5984          * @param {Roo.bootstrap.Table} this
5985          * @param {Number} columnIndex
5986          * @param {Roo.EventObject} e
5987          */
5988         "headercontextmenu" : true
5989     });
5990 };
5991
5992 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5993     
5994     cls: false,
5995     align: false,
5996     bgcolor: false,
5997     border: false,
5998     cellpadding: false,
5999     cellspacing: false,
6000     frame: false,
6001     rules: false,
6002     sortable: false,
6003     summary: false,
6004     width: false,
6005     striped : false,
6006     scrollBody : false,
6007     bordered: false,
6008     hover:  false,
6009     condensed : false,
6010     responsive : false,
6011     sm : false,
6012     cm : false,
6013     store : false,
6014     loadMask : false,
6015     footerShow : true,
6016     headerShow : true,
6017   
6018     rowSelection : false,
6019     cellSelection : false,
6020     layout : false,
6021     
6022     // Roo.Element - the tbody
6023     mainBody: false,
6024     // Roo.Element - thead element
6025     mainHead: false,
6026     
6027     container: false, // used by gridpanel...
6028     
6029     lazyLoad : false,
6030     
6031     getAutoCreate : function()
6032     {
6033         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6034         
6035         cfg = {
6036             tag: 'table',
6037             cls : 'table',
6038             cn : []
6039         };
6040         if (this.scrollBody) {
6041             cfg.cls += ' table-body-fixed';
6042         }    
6043         if (this.striped) {
6044             cfg.cls += ' table-striped';
6045         }
6046         
6047         if (this.hover) {
6048             cfg.cls += ' table-hover';
6049         }
6050         if (this.bordered) {
6051             cfg.cls += ' table-bordered';
6052         }
6053         if (this.condensed) {
6054             cfg.cls += ' table-condensed';
6055         }
6056         if (this.responsive) {
6057             cfg.cls += ' table-responsive';
6058         }
6059         
6060         if (this.cls) {
6061             cfg.cls+=  ' ' +this.cls;
6062         }
6063         
6064         // this lot should be simplifed...
6065         
6066         if (this.align) {
6067             cfg.align=this.align;
6068         }
6069         if (this.bgcolor) {
6070             cfg.bgcolor=this.bgcolor;
6071         }
6072         if (this.border) {
6073             cfg.border=this.border;
6074         }
6075         if (this.cellpadding) {
6076             cfg.cellpadding=this.cellpadding;
6077         }
6078         if (this.cellspacing) {
6079             cfg.cellspacing=this.cellspacing;
6080         }
6081         if (this.frame) {
6082             cfg.frame=this.frame;
6083         }
6084         if (this.rules) {
6085             cfg.rules=this.rules;
6086         }
6087         if (this.sortable) {
6088             cfg.sortable=this.sortable;
6089         }
6090         if (this.summary) {
6091             cfg.summary=this.summary;
6092         }
6093         if (this.width) {
6094             cfg.width=this.width;
6095         }
6096         if (this.layout) {
6097             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6098         }
6099         
6100         if(this.store || this.cm){
6101             if(this.headerShow){
6102                 cfg.cn.push(this.renderHeader());
6103             }
6104             
6105             cfg.cn.push(this.renderBody());
6106             
6107             if(this.footerShow){
6108                 cfg.cn.push(this.renderFooter());
6109             }
6110             // where does this come from?
6111             //cfg.cls+=  ' TableGrid';
6112         }
6113         
6114         return { cn : [ cfg ] };
6115     },
6116     
6117     initEvents : function()
6118     {   
6119         if(!this.store || !this.cm){
6120             return;
6121         }
6122         if (this.selModel) {
6123             this.selModel.initEvents();
6124         }
6125         
6126         
6127         //Roo.log('initEvents with ds!!!!');
6128         
6129         this.mainBody = this.el.select('tbody', true).first();
6130         this.mainHead = this.el.select('thead', true).first();
6131         
6132         
6133         
6134         
6135         var _this = this;
6136         
6137         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6138             e.on('click', _this.sort, _this);
6139         });
6140         
6141         this.mainBody.on("click", this.onClick, this);
6142         this.mainBody.on("dblclick", this.onDblClick, this);
6143         
6144         // why is this done????? = it breaks dialogs??
6145         //this.parent().el.setStyle('position', 'relative');
6146         
6147         
6148         if (this.footer) {
6149             this.footer.parentId = this.id;
6150             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6151             
6152             if(this.lazyLoad){
6153                 this.el.select('tfoot tr td').first().addClass('hide');
6154             }
6155         } 
6156         
6157         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6158         
6159         this.store.on('load', this.onLoad, this);
6160         this.store.on('beforeload', this.onBeforeLoad, this);
6161         this.store.on('update', this.onUpdate, this);
6162         this.store.on('add', this.onAdd, this);
6163         this.store.on("clear", this.clear, this);
6164         
6165         this.el.on("contextmenu", this.onContextMenu, this);
6166         
6167         this.mainBody.on('scroll', this.onBodyScroll, this);
6168         
6169         this.cm.on("headerchange", this.onHeaderChange, this);
6170         
6171     },
6172     
6173     onContextMenu : function(e, t)
6174     {
6175         this.processEvent("contextmenu", e);
6176     },
6177     
6178     processEvent : function(name, e)
6179     {
6180         if (name != 'touchstart' ) {
6181             this.fireEvent(name, e);    
6182         }
6183         
6184         var t = e.getTarget();
6185         
6186         var cell = Roo.get(t);
6187         
6188         if(!cell){
6189             return;
6190         }
6191         
6192         if(cell.findParent('tfoot', false, true)){
6193             return;
6194         }
6195         
6196         if(cell.findParent('thead', false, true)){
6197             
6198             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6199                 cell = Roo.get(t).findParent('th', false, true);
6200                 if (!cell) {
6201                     Roo.log("failed to find th in thead?");
6202                     Roo.log(e.getTarget());
6203                     return;
6204                 }
6205             }
6206             
6207             var cellIndex = cell.dom.cellIndex;
6208             
6209             var ename = name == 'touchstart' ? 'click' : name;
6210             this.fireEvent("header" + ename, this, cellIndex, e);
6211             
6212             return;
6213         }
6214         
6215         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6216             cell = Roo.get(t).findParent('td', false, true);
6217             if (!cell) {
6218                 Roo.log("failed to find th in tbody?");
6219                 Roo.log(e.getTarget());
6220                 return;
6221             }
6222         }
6223         
6224         var row = cell.findParent('tr', false, true);
6225         var cellIndex = cell.dom.cellIndex;
6226         var rowIndex = row.dom.rowIndex - 1;
6227         
6228         if(row !== false){
6229             
6230             this.fireEvent("row" + name, this, rowIndex, e);
6231             
6232             if(cell !== false){
6233             
6234                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6235             }
6236         }
6237         
6238     },
6239     
6240     onMouseover : function(e, el)
6241     {
6242         var cell = Roo.get(el);
6243         
6244         if(!cell){
6245             return;
6246         }
6247         
6248         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6249             cell = cell.findParent('td', false, true);
6250         }
6251         
6252         var row = cell.findParent('tr', false, true);
6253         var cellIndex = cell.dom.cellIndex;
6254         var rowIndex = row.dom.rowIndex - 1; // start from 0
6255         
6256         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6257         
6258     },
6259     
6260     onMouseout : function(e, el)
6261     {
6262         var cell = Roo.get(el);
6263         
6264         if(!cell){
6265             return;
6266         }
6267         
6268         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6269             cell = cell.findParent('td', false, true);
6270         }
6271         
6272         var row = cell.findParent('tr', false, true);
6273         var cellIndex = cell.dom.cellIndex;
6274         var rowIndex = row.dom.rowIndex - 1; // start from 0
6275         
6276         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6277         
6278     },
6279     
6280     onClick : function(e, el)
6281     {
6282         var cell = Roo.get(el);
6283         
6284         if(!cell || (!this.cellSelection && !this.rowSelection)){
6285             return;
6286         }
6287         
6288         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6289             cell = cell.findParent('td', false, true);
6290         }
6291         
6292         if(!cell || typeof(cell) == 'undefined'){
6293             return;
6294         }
6295         
6296         var row = cell.findParent('tr', false, true);
6297         
6298         if(!row || typeof(row) == 'undefined'){
6299             return;
6300         }
6301         
6302         var cellIndex = cell.dom.cellIndex;
6303         var rowIndex = this.getRowIndex(row);
6304         
6305         // why??? - should these not be based on SelectionModel?
6306         if(this.cellSelection){
6307             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6308         }
6309         
6310         if(this.rowSelection){
6311             this.fireEvent('rowclick', this, row, rowIndex, e);
6312         }
6313         
6314         
6315     },
6316         
6317     onDblClick : function(e,el)
6318     {
6319         var cell = Roo.get(el);
6320         
6321         if(!cell || (!this.cellSelection && !this.rowSelection)){
6322             return;
6323         }
6324         
6325         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6326             cell = cell.findParent('td', false, true);
6327         }
6328         
6329         if(!cell || typeof(cell) == 'undefined'){
6330             return;
6331         }
6332         
6333         var row = cell.findParent('tr', false, true);
6334         
6335         if(!row || typeof(row) == 'undefined'){
6336             return;
6337         }
6338         
6339         var cellIndex = cell.dom.cellIndex;
6340         var rowIndex = this.getRowIndex(row);
6341         
6342         if(this.cellSelection){
6343             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6344         }
6345         
6346         if(this.rowSelection){
6347             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6348         }
6349     },
6350     
6351     sort : function(e,el)
6352     {
6353         var col = Roo.get(el);
6354         
6355         if(!col.hasClass('sortable')){
6356             return;
6357         }
6358         
6359         var sort = col.attr('sort');
6360         var dir = 'ASC';
6361         
6362         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6363             dir = 'DESC';
6364         }
6365         
6366         this.store.sortInfo = {field : sort, direction : dir};
6367         
6368         if (this.footer) {
6369             Roo.log("calling footer first");
6370             this.footer.onClick('first');
6371         } else {
6372         
6373             this.store.load({ params : { start : 0 } });
6374         }
6375     },
6376     
6377     renderHeader : function()
6378     {
6379         var header = {
6380             tag: 'thead',
6381             cn : []
6382         };
6383         
6384         var cm = this.cm;
6385         this.totalWidth = 0;
6386         
6387         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6388             
6389             var config = cm.config[i];
6390             
6391             var c = {
6392                 tag: 'th',
6393                 style : '',
6394                 html: cm.getColumnHeader(i)
6395             };
6396             
6397             var hh = '';
6398             
6399             if(typeof(config.sortable) != 'undefined' && config.sortable){
6400                 c.cls = 'sortable';
6401                 c.html = '<i class="glyphicon"></i>' + c.html;
6402             }
6403             
6404             if(typeof(config.lgHeader) != 'undefined'){
6405                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6406             }
6407             
6408             if(typeof(config.mdHeader) != 'undefined'){
6409                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6410             }
6411             
6412             if(typeof(config.smHeader) != 'undefined'){
6413                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6414             }
6415             
6416             if(typeof(config.xsHeader) != 'undefined'){
6417                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6418             }
6419             
6420             if(hh.length){
6421                 c.html = hh;
6422             }
6423             
6424             if(typeof(config.tooltip) != 'undefined'){
6425                 c.tooltip = config.tooltip;
6426             }
6427             
6428             if(typeof(config.colspan) != 'undefined'){
6429                 c.colspan = config.colspan;
6430             }
6431             
6432             if(typeof(config.hidden) != 'undefined' && config.hidden){
6433                 c.style += ' display:none;';
6434             }
6435             
6436             if(typeof(config.dataIndex) != 'undefined'){
6437                 c.sort = config.dataIndex;
6438             }
6439             
6440            
6441             
6442             if(typeof(config.align) != 'undefined' && config.align.length){
6443                 c.style += ' text-align:' + config.align + ';';
6444             }
6445             
6446             if(typeof(config.width) != 'undefined'){
6447                 c.style += ' width:' + config.width + 'px;';
6448                 this.totalWidth += config.width;
6449             } else {
6450                 this.totalWidth += 100; // assume minimum of 100 per column?
6451             }
6452             
6453             if(typeof(config.cls) != 'undefined'){
6454                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6455             }
6456             
6457             ['xs','sm','md','lg'].map(function(size){
6458                 
6459                 if(typeof(config[size]) == 'undefined'){
6460                     return;
6461                 }
6462                 
6463                 if (!config[size]) { // 0 = hidden
6464                     c.cls += ' hidden-' + size;
6465                     return;
6466                 }
6467                 
6468                 c.cls += ' col-' + size + '-' + config[size];
6469
6470             });
6471             
6472             header.cn.push(c)
6473         }
6474         
6475         return header;
6476     },
6477     
6478     renderBody : function()
6479     {
6480         var body = {
6481             tag: 'tbody',
6482             cn : [
6483                 {
6484                     tag: 'tr',
6485                     cn : [
6486                         {
6487                             tag : 'td',
6488                             colspan :  this.cm.getColumnCount()
6489                         }
6490                     ]
6491                 }
6492             ]
6493         };
6494         
6495         return body;
6496     },
6497     
6498     renderFooter : function()
6499     {
6500         var footer = {
6501             tag: 'tfoot',
6502             cn : [
6503                 {
6504                     tag: 'tr',
6505                     cn : [
6506                         {
6507                             tag : 'td',
6508                             colspan :  this.cm.getColumnCount()
6509                         }
6510                     ]
6511                 }
6512             ]
6513         };
6514         
6515         return footer;
6516     },
6517     
6518     
6519     
6520     onLoad : function()
6521     {
6522 //        Roo.log('ds onload');
6523         this.clear();
6524         
6525         var _this = this;
6526         var cm = this.cm;
6527         var ds = this.store;
6528         
6529         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6530             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6531             if (_this.store.sortInfo) {
6532                     
6533                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6534                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6535                 }
6536                 
6537                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6538                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6539                 }
6540             }
6541         });
6542         
6543         var tbody =  this.mainBody;
6544               
6545         if(ds.getCount() > 0){
6546             ds.data.each(function(d,rowIndex){
6547                 var row =  this.renderRow(cm, ds, rowIndex);
6548                 
6549                 tbody.createChild(row);
6550                 
6551                 var _this = this;
6552                 
6553                 if(row.cellObjects.length){
6554                     Roo.each(row.cellObjects, function(r){
6555                         _this.renderCellObject(r);
6556                     })
6557                 }
6558                 
6559             }, this);
6560         }
6561         
6562         Roo.each(this.el.select('tbody td', true).elements, function(e){
6563             e.on('mouseover', _this.onMouseover, _this);
6564         });
6565         
6566         Roo.each(this.el.select('tbody td', true).elements, function(e){
6567             e.on('mouseout', _this.onMouseout, _this);
6568         });
6569         this.fireEvent('rowsrendered', this);
6570         //if(this.loadMask){
6571         //    this.maskEl.hide();
6572         //}
6573         
6574         this.autoSize();
6575     },
6576     
6577     
6578     onUpdate : function(ds,record)
6579     {
6580         this.refreshRow(record);
6581         this.autoSize();
6582     },
6583     
6584     onRemove : function(ds, record, index, isUpdate){
6585         if(isUpdate !== true){
6586             this.fireEvent("beforerowremoved", this, index, record);
6587         }
6588         var bt = this.mainBody.dom;
6589         
6590         var rows = this.el.select('tbody > tr', true).elements;
6591         
6592         if(typeof(rows[index]) != 'undefined'){
6593             bt.removeChild(rows[index].dom);
6594         }
6595         
6596 //        if(bt.rows[index]){
6597 //            bt.removeChild(bt.rows[index]);
6598 //        }
6599         
6600         if(isUpdate !== true){
6601             //this.stripeRows(index);
6602             //this.syncRowHeights(index, index);
6603             //this.layout();
6604             this.fireEvent("rowremoved", this, index, record);
6605         }
6606     },
6607     
6608     onAdd : function(ds, records, rowIndex)
6609     {
6610         //Roo.log('on Add called');
6611         // - note this does not handle multiple adding very well..
6612         var bt = this.mainBody.dom;
6613         for (var i =0 ; i < records.length;i++) {
6614             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6615             //Roo.log(records[i]);
6616             //Roo.log(this.store.getAt(rowIndex+i));
6617             this.insertRow(this.store, rowIndex + i, false);
6618             return;
6619         }
6620         
6621     },
6622     
6623     
6624     refreshRow : function(record){
6625         var ds = this.store, index;
6626         if(typeof record == 'number'){
6627             index = record;
6628             record = ds.getAt(index);
6629         }else{
6630             index = ds.indexOf(record);
6631         }
6632         this.insertRow(ds, index, true);
6633         this.autoSize();
6634         this.onRemove(ds, record, index+1, true);
6635         this.autoSize();
6636         //this.syncRowHeights(index, index);
6637         //this.layout();
6638         this.fireEvent("rowupdated", this, index, record);
6639     },
6640     
6641     insertRow : function(dm, rowIndex, isUpdate){
6642         
6643         if(!isUpdate){
6644             this.fireEvent("beforerowsinserted", this, rowIndex);
6645         }
6646             //var s = this.getScrollState();
6647         var row = this.renderRow(this.cm, this.store, rowIndex);
6648         // insert before rowIndex..
6649         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6650         
6651         var _this = this;
6652                 
6653         if(row.cellObjects.length){
6654             Roo.each(row.cellObjects, function(r){
6655                 _this.renderCellObject(r);
6656             })
6657         }
6658             
6659         if(!isUpdate){
6660             this.fireEvent("rowsinserted", this, rowIndex);
6661             //this.syncRowHeights(firstRow, lastRow);
6662             //this.stripeRows(firstRow);
6663             //this.layout();
6664         }
6665         
6666     },
6667     
6668     
6669     getRowDom : function(rowIndex)
6670     {
6671         var rows = this.el.select('tbody > tr', true).elements;
6672         
6673         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6674         
6675     },
6676     // returns the object tree for a tr..
6677   
6678     
6679     renderRow : function(cm, ds, rowIndex) 
6680     {
6681         
6682         var d = ds.getAt(rowIndex);
6683         
6684         var row = {
6685             tag : 'tr',
6686             cn : []
6687         };
6688             
6689         var cellObjects = [];
6690         
6691         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6692             var config = cm.config[i];
6693             
6694             var renderer = cm.getRenderer(i);
6695             var value = '';
6696             var id = false;
6697             
6698             if(typeof(renderer) !== 'undefined'){
6699                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6700             }
6701             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6702             // and are rendered into the cells after the row is rendered - using the id for the element.
6703             
6704             if(typeof(value) === 'object'){
6705                 id = Roo.id();
6706                 cellObjects.push({
6707                     container : id,
6708                     cfg : value 
6709                 })
6710             }
6711             
6712             var rowcfg = {
6713                 record: d,
6714                 rowIndex : rowIndex,
6715                 colIndex : i,
6716                 rowClass : ''
6717             };
6718
6719             this.fireEvent('rowclass', this, rowcfg);
6720             
6721             var td = {
6722                 tag: 'td',
6723                 cls : rowcfg.rowClass,
6724                 style: '',
6725                 html: (typeof(value) === 'object') ? '' : value
6726             };
6727             
6728             if (id) {
6729                 td.id = id;
6730             }
6731             
6732             if(typeof(config.colspan) != 'undefined'){
6733                 td.colspan = config.colspan;
6734             }
6735             
6736             if(typeof(config.hidden) != 'undefined' && config.hidden){
6737                 td.style += ' display:none;';
6738             }
6739             
6740             if(typeof(config.align) != 'undefined' && config.align.length){
6741                 td.style += ' text-align:' + config.align + ';';
6742             }
6743             
6744             if(typeof(config.width) != 'undefined'){
6745                 td.style += ' width:' +  config.width + 'px;';
6746             }
6747             
6748             if(typeof(config.cursor) != 'undefined'){
6749                 td.style += ' cursor:' +  config.cursor + ';';
6750             }
6751             
6752             if(typeof(config.cls) != 'undefined'){
6753                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6754             }
6755             
6756             ['xs','sm','md','lg'].map(function(size){
6757                 
6758                 if(typeof(config[size]) == 'undefined'){
6759                     return;
6760                 }
6761                 
6762                 if (!config[size]) { // 0 = hidden
6763                     td.cls += ' hidden-' + size;
6764                     return;
6765                 }
6766                 
6767                 td.cls += ' col-' + size + '-' + config[size];
6768
6769             });
6770              
6771             row.cn.push(td);
6772            
6773         }
6774         
6775         row.cellObjects = cellObjects;
6776         
6777         return row;
6778           
6779     },
6780     
6781     
6782     
6783     onBeforeLoad : function()
6784     {
6785         //Roo.log('ds onBeforeLoad');
6786         
6787         //this.clear();
6788         
6789         //if(this.loadMask){
6790         //    this.maskEl.show();
6791         //}
6792     },
6793      /**
6794      * Remove all rows
6795      */
6796     clear : function()
6797     {
6798         this.el.select('tbody', true).first().dom.innerHTML = '';
6799     },
6800     /**
6801      * Show or hide a row.
6802      * @param {Number} rowIndex to show or hide
6803      * @param {Boolean} state hide
6804      */
6805     setRowVisibility : function(rowIndex, state)
6806     {
6807         var bt = this.mainBody.dom;
6808         
6809         var rows = this.el.select('tbody > tr', true).elements;
6810         
6811         if(typeof(rows[rowIndex]) == 'undefined'){
6812             return;
6813         }
6814         rows[rowIndex].dom.style.display = state ? '' : 'none';
6815     },
6816     
6817     
6818     getSelectionModel : function(){
6819         if(!this.selModel){
6820             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6821         }
6822         return this.selModel;
6823     },
6824     /*
6825      * Render the Roo.bootstrap object from renderder
6826      */
6827     renderCellObject : function(r)
6828     {
6829         var _this = this;
6830         
6831         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6832         
6833         var t = r.cfg.render(r.container);
6834         
6835         if(r.cfg.cn){
6836             Roo.each(r.cfg.cn, function(c){
6837                 var child = {
6838                     container: t.getChildContainer(),
6839                     cfg: c
6840                 };
6841                 _this.renderCellObject(child);
6842             })
6843         }
6844     },
6845     
6846     getRowIndex : function(row)
6847     {
6848         var rowIndex = -1;
6849         
6850         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6851             if(el != row){
6852                 return;
6853             }
6854             
6855             rowIndex = index;
6856         });
6857         
6858         return rowIndex;
6859     },
6860      /**
6861      * Returns the grid's underlying element = used by panel.Grid
6862      * @return {Element} The element
6863      */
6864     getGridEl : function(){
6865         return this.el;
6866     },
6867      /**
6868      * Forces a resize - used by panel.Grid
6869      * @return {Element} The element
6870      */
6871     autoSize : function()
6872     {
6873         //var ctr = Roo.get(this.container.dom.parentElement);
6874         var ctr = Roo.get(this.el.dom);
6875         
6876         var thd = this.getGridEl().select('thead',true).first();
6877         var tbd = this.getGridEl().select('tbody', true).first();
6878         var tfd = this.getGridEl().select('tfoot', true).first();
6879         
6880         var cw = ctr.getWidth();
6881         
6882         if (tbd) {
6883             
6884             tbd.setSize(ctr.getWidth(),
6885                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6886             );
6887             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6888             cw -= barsize;
6889         }
6890         cw = Math.max(cw, this.totalWidth);
6891         this.getGridEl().select('tr',true).setWidth(cw);
6892         // resize 'expandable coloumn?
6893         
6894         return; // we doe not have a view in this design..
6895         
6896     },
6897     onBodyScroll: function()
6898     {
6899         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6900         if(this.mainHead){
6901             this.mainHead.setStyle({
6902                 'position' : 'relative',
6903                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6904             });
6905         }
6906         
6907         if(this.lazyLoad){
6908             
6909             var scrollHeight = this.mainBody.dom.scrollHeight;
6910             
6911             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6912             
6913             var height = this.mainBody.getHeight();
6914             
6915             if(scrollHeight - height == scrollTop) {
6916                 
6917                 var total = this.ds.getTotalCount();
6918                 
6919                 if(this.footer.cursor + this.footer.pageSize < total){
6920                     
6921                     this.footer.ds.load({
6922                         params : {
6923                             start : this.footer.cursor + this.footer.pageSize,
6924                             limit : this.footer.pageSize
6925                         },
6926                         add : true
6927                     });
6928                 }
6929             }
6930             
6931         }
6932     },
6933     
6934     onHeaderChange : function()
6935     {
6936         
6937         var header = this.renderHeader();
6938         var table = this.el.select('table', true).first();
6939         
6940         this.mainHead.remove();
6941         this.mainHead = table.createChild(header, this.mainBody, false);
6942     }
6943     
6944 });
6945
6946  
6947
6948  /*
6949  * - LGPL
6950  *
6951  * table cell
6952  * 
6953  */
6954
6955 /**
6956  * @class Roo.bootstrap.TableCell
6957  * @extends Roo.bootstrap.Component
6958  * Bootstrap TableCell class
6959  * @cfg {String} html cell contain text
6960  * @cfg {String} cls cell class
6961  * @cfg {String} tag cell tag (td|th) default td
6962  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6963  * @cfg {String} align Aligns the content in a cell
6964  * @cfg {String} axis Categorizes cells
6965  * @cfg {String} bgcolor Specifies the background color of a cell
6966  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6967  * @cfg {Number} colspan Specifies the number of columns a cell should span
6968  * @cfg {String} headers Specifies one or more header cells a cell is related to
6969  * @cfg {Number} height Sets the height of a cell
6970  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6971  * @cfg {Number} rowspan Sets the number of rows a cell should span
6972  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6973  * @cfg {String} valign Vertical aligns the content in a cell
6974  * @cfg {Number} width Specifies the width of a cell
6975  * 
6976  * @constructor
6977  * Create a new TableCell
6978  * @param {Object} config The config object
6979  */
6980
6981 Roo.bootstrap.TableCell = function(config){
6982     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6983 };
6984
6985 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6986     
6987     html: false,
6988     cls: false,
6989     tag: false,
6990     abbr: false,
6991     align: false,
6992     axis: false,
6993     bgcolor: false,
6994     charoff: false,
6995     colspan: false,
6996     headers: false,
6997     height: false,
6998     nowrap: false,
6999     rowspan: false,
7000     scope: false,
7001     valign: false,
7002     width: false,
7003     
7004     
7005     getAutoCreate : function(){
7006         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7007         
7008         cfg = {
7009             tag: 'td'
7010         };
7011         
7012         if(this.tag){
7013             cfg.tag = this.tag;
7014         }
7015         
7016         if (this.html) {
7017             cfg.html=this.html
7018         }
7019         if (this.cls) {
7020             cfg.cls=this.cls
7021         }
7022         if (this.abbr) {
7023             cfg.abbr=this.abbr
7024         }
7025         if (this.align) {
7026             cfg.align=this.align
7027         }
7028         if (this.axis) {
7029             cfg.axis=this.axis
7030         }
7031         if (this.bgcolor) {
7032             cfg.bgcolor=this.bgcolor
7033         }
7034         if (this.charoff) {
7035             cfg.charoff=this.charoff
7036         }
7037         if (this.colspan) {
7038             cfg.colspan=this.colspan
7039         }
7040         if (this.headers) {
7041             cfg.headers=this.headers
7042         }
7043         if (this.height) {
7044             cfg.height=this.height
7045         }
7046         if (this.nowrap) {
7047             cfg.nowrap=this.nowrap
7048         }
7049         if (this.rowspan) {
7050             cfg.rowspan=this.rowspan
7051         }
7052         if (this.scope) {
7053             cfg.scope=this.scope
7054         }
7055         if (this.valign) {
7056             cfg.valign=this.valign
7057         }
7058         if (this.width) {
7059             cfg.width=this.width
7060         }
7061         
7062         
7063         return cfg;
7064     }
7065    
7066 });
7067
7068  
7069
7070  /*
7071  * - LGPL
7072  *
7073  * table row
7074  * 
7075  */
7076
7077 /**
7078  * @class Roo.bootstrap.TableRow
7079  * @extends Roo.bootstrap.Component
7080  * Bootstrap TableRow class
7081  * @cfg {String} cls row class
7082  * @cfg {String} align Aligns the content in a table row
7083  * @cfg {String} bgcolor Specifies a background color for a table row
7084  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7085  * @cfg {String} valign Vertical aligns the content in a table row
7086  * 
7087  * @constructor
7088  * Create a new TableRow
7089  * @param {Object} config The config object
7090  */
7091
7092 Roo.bootstrap.TableRow = function(config){
7093     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7094 };
7095
7096 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7097     
7098     cls: false,
7099     align: false,
7100     bgcolor: false,
7101     charoff: false,
7102     valign: false,
7103     
7104     getAutoCreate : function(){
7105         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7106         
7107         cfg = {
7108             tag: 'tr'
7109         };
7110             
7111         if(this.cls){
7112             cfg.cls = this.cls;
7113         }
7114         if(this.align){
7115             cfg.align = this.align;
7116         }
7117         if(this.bgcolor){
7118             cfg.bgcolor = this.bgcolor;
7119         }
7120         if(this.charoff){
7121             cfg.charoff = this.charoff;
7122         }
7123         if(this.valign){
7124             cfg.valign = this.valign;
7125         }
7126         
7127         return cfg;
7128     }
7129    
7130 });
7131
7132  
7133
7134  /*
7135  * - LGPL
7136  *
7137  * table body
7138  * 
7139  */
7140
7141 /**
7142  * @class Roo.bootstrap.TableBody
7143  * @extends Roo.bootstrap.Component
7144  * Bootstrap TableBody class
7145  * @cfg {String} cls element class
7146  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7147  * @cfg {String} align Aligns the content inside the element
7148  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7149  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7150  * 
7151  * @constructor
7152  * Create a new TableBody
7153  * @param {Object} config The config object
7154  */
7155
7156 Roo.bootstrap.TableBody = function(config){
7157     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7158 };
7159
7160 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7161     
7162     cls: false,
7163     tag: false,
7164     align: false,
7165     charoff: false,
7166     valign: false,
7167     
7168     getAutoCreate : function(){
7169         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7170         
7171         cfg = {
7172             tag: 'tbody'
7173         };
7174             
7175         if (this.cls) {
7176             cfg.cls=this.cls
7177         }
7178         if(this.tag){
7179             cfg.tag = this.tag;
7180         }
7181         
7182         if(this.align){
7183             cfg.align = this.align;
7184         }
7185         if(this.charoff){
7186             cfg.charoff = this.charoff;
7187         }
7188         if(this.valign){
7189             cfg.valign = this.valign;
7190         }
7191         
7192         return cfg;
7193     }
7194     
7195     
7196 //    initEvents : function()
7197 //    {
7198 //        
7199 //        if(!this.store){
7200 //            return;
7201 //        }
7202 //        
7203 //        this.store = Roo.factory(this.store, Roo.data);
7204 //        this.store.on('load', this.onLoad, this);
7205 //        
7206 //        this.store.load();
7207 //        
7208 //    },
7209 //    
7210 //    onLoad: function () 
7211 //    {   
7212 //        this.fireEvent('load', this);
7213 //    }
7214 //    
7215 //   
7216 });
7217
7218  
7219
7220  /*
7221  * Based on:
7222  * Ext JS Library 1.1.1
7223  * Copyright(c) 2006-2007, Ext JS, LLC.
7224  *
7225  * Originally Released Under LGPL - original licence link has changed is not relivant.
7226  *
7227  * Fork - LGPL
7228  * <script type="text/javascript">
7229  */
7230
7231 // as we use this in bootstrap.
7232 Roo.namespace('Roo.form');
7233  /**
7234  * @class Roo.form.Action
7235  * Internal Class used to handle form actions
7236  * @constructor
7237  * @param {Roo.form.BasicForm} el The form element or its id
7238  * @param {Object} config Configuration options
7239  */
7240
7241  
7242  
7243 // define the action interface
7244 Roo.form.Action = function(form, options){
7245     this.form = form;
7246     this.options = options || {};
7247 };
7248 /**
7249  * Client Validation Failed
7250  * @const 
7251  */
7252 Roo.form.Action.CLIENT_INVALID = 'client';
7253 /**
7254  * Server Validation Failed
7255  * @const 
7256  */
7257 Roo.form.Action.SERVER_INVALID = 'server';
7258  /**
7259  * Connect to Server Failed
7260  * @const 
7261  */
7262 Roo.form.Action.CONNECT_FAILURE = 'connect';
7263 /**
7264  * Reading Data from Server Failed
7265  * @const 
7266  */
7267 Roo.form.Action.LOAD_FAILURE = 'load';
7268
7269 Roo.form.Action.prototype = {
7270     type : 'default',
7271     failureType : undefined,
7272     response : undefined,
7273     result : undefined,
7274
7275     // interface method
7276     run : function(options){
7277
7278     },
7279
7280     // interface method
7281     success : function(response){
7282
7283     },
7284
7285     // interface method
7286     handleResponse : function(response){
7287
7288     },
7289
7290     // default connection failure
7291     failure : function(response){
7292         
7293         this.response = response;
7294         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7295         this.form.afterAction(this, false);
7296     },
7297
7298     processResponse : function(response){
7299         this.response = response;
7300         if(!response.responseText){
7301             return true;
7302         }
7303         this.result = this.handleResponse(response);
7304         return this.result;
7305     },
7306
7307     // utility functions used internally
7308     getUrl : function(appendParams){
7309         var url = this.options.url || this.form.url || this.form.el.dom.action;
7310         if(appendParams){
7311             var p = this.getParams();
7312             if(p){
7313                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7314             }
7315         }
7316         return url;
7317     },
7318
7319     getMethod : function(){
7320         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7321     },
7322
7323     getParams : function(){
7324         var bp = this.form.baseParams;
7325         var p = this.options.params;
7326         if(p){
7327             if(typeof p == "object"){
7328                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7329             }else if(typeof p == 'string' && bp){
7330                 p += '&' + Roo.urlEncode(bp);
7331             }
7332         }else if(bp){
7333             p = Roo.urlEncode(bp);
7334         }
7335         return p;
7336     },
7337
7338     createCallback : function(){
7339         return {
7340             success: this.success,
7341             failure: this.failure,
7342             scope: this,
7343             timeout: (this.form.timeout*1000),
7344             upload: this.form.fileUpload ? this.success : undefined
7345         };
7346     }
7347 };
7348
7349 Roo.form.Action.Submit = function(form, options){
7350     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7351 };
7352
7353 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7354     type : 'submit',
7355
7356     haveProgress : false,
7357     uploadComplete : false,
7358     
7359     // uploadProgress indicator.
7360     uploadProgress : function()
7361     {
7362         if (!this.form.progressUrl) {
7363             return;
7364         }
7365         
7366         if (!this.haveProgress) {
7367             Roo.MessageBox.progress("Uploading", "Uploading");
7368         }
7369         if (this.uploadComplete) {
7370            Roo.MessageBox.hide();
7371            return;
7372         }
7373         
7374         this.haveProgress = true;
7375    
7376         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7377         
7378         var c = new Roo.data.Connection();
7379         c.request({
7380             url : this.form.progressUrl,
7381             params: {
7382                 id : uid
7383             },
7384             method: 'GET',
7385             success : function(req){
7386                //console.log(data);
7387                 var rdata = false;
7388                 var edata;
7389                 try  {
7390                    rdata = Roo.decode(req.responseText)
7391                 } catch (e) {
7392                     Roo.log("Invalid data from server..");
7393                     Roo.log(edata);
7394                     return;
7395                 }
7396                 if (!rdata || !rdata.success) {
7397                     Roo.log(rdata);
7398                     Roo.MessageBox.alert(Roo.encode(rdata));
7399                     return;
7400                 }
7401                 var data = rdata.data;
7402                 
7403                 if (this.uploadComplete) {
7404                    Roo.MessageBox.hide();
7405                    return;
7406                 }
7407                    
7408                 if (data){
7409                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7410                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7411                     );
7412                 }
7413                 this.uploadProgress.defer(2000,this);
7414             },
7415        
7416             failure: function(data) {
7417                 Roo.log('progress url failed ');
7418                 Roo.log(data);
7419             },
7420             scope : this
7421         });
7422            
7423     },
7424     
7425     
7426     run : function()
7427     {
7428         // run get Values on the form, so it syncs any secondary forms.
7429         this.form.getValues();
7430         
7431         var o = this.options;
7432         var method = this.getMethod();
7433         var isPost = method == 'POST';
7434         if(o.clientValidation === false || this.form.isValid()){
7435             
7436             if (this.form.progressUrl) {
7437                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7438                     (new Date() * 1) + '' + Math.random());
7439                     
7440             } 
7441             
7442             
7443             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7444                 form:this.form.el.dom,
7445                 url:this.getUrl(!isPost),
7446                 method: method,
7447                 params:isPost ? this.getParams() : null,
7448                 isUpload: this.form.fileUpload
7449             }));
7450             
7451             this.uploadProgress();
7452
7453         }else if (o.clientValidation !== false){ // client validation failed
7454             this.failureType = Roo.form.Action.CLIENT_INVALID;
7455             this.form.afterAction(this, false);
7456         }
7457     },
7458
7459     success : function(response)
7460     {
7461         this.uploadComplete= true;
7462         if (this.haveProgress) {
7463             Roo.MessageBox.hide();
7464         }
7465         
7466         
7467         var result = this.processResponse(response);
7468         if(result === true || result.success){
7469             this.form.afterAction(this, true);
7470             return;
7471         }
7472         if(result.errors){
7473             this.form.markInvalid(result.errors);
7474             this.failureType = Roo.form.Action.SERVER_INVALID;
7475         }
7476         this.form.afterAction(this, false);
7477     },
7478     failure : function(response)
7479     {
7480         this.uploadComplete= true;
7481         if (this.haveProgress) {
7482             Roo.MessageBox.hide();
7483         }
7484         
7485         this.response = response;
7486         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7487         this.form.afterAction(this, false);
7488     },
7489     
7490     handleResponse : function(response){
7491         if(this.form.errorReader){
7492             var rs = this.form.errorReader.read(response);
7493             var errors = [];
7494             if(rs.records){
7495                 for(var i = 0, len = rs.records.length; i < len; i++) {
7496                     var r = rs.records[i];
7497                     errors[i] = r.data;
7498                 }
7499             }
7500             if(errors.length < 1){
7501                 errors = null;
7502             }
7503             return {
7504                 success : rs.success,
7505                 errors : errors
7506             };
7507         }
7508         var ret = false;
7509         try {
7510             ret = Roo.decode(response.responseText);
7511         } catch (e) {
7512             ret = {
7513                 success: false,
7514                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7515                 errors : []
7516             };
7517         }
7518         return ret;
7519         
7520     }
7521 });
7522
7523
7524 Roo.form.Action.Load = function(form, options){
7525     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7526     this.reader = this.form.reader;
7527 };
7528
7529 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7530     type : 'load',
7531
7532     run : function(){
7533         
7534         Roo.Ajax.request(Roo.apply(
7535                 this.createCallback(), {
7536                     method:this.getMethod(),
7537                     url:this.getUrl(false),
7538                     params:this.getParams()
7539         }));
7540     },
7541
7542     success : function(response){
7543         
7544         var result = this.processResponse(response);
7545         if(result === true || !result.success || !result.data){
7546             this.failureType = Roo.form.Action.LOAD_FAILURE;
7547             this.form.afterAction(this, false);
7548             return;
7549         }
7550         this.form.clearInvalid();
7551         this.form.setValues(result.data);
7552         this.form.afterAction(this, true);
7553     },
7554
7555     handleResponse : function(response){
7556         if(this.form.reader){
7557             var rs = this.form.reader.read(response);
7558             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7559             return {
7560                 success : rs.success,
7561                 data : data
7562             };
7563         }
7564         return Roo.decode(response.responseText);
7565     }
7566 });
7567
7568 Roo.form.Action.ACTION_TYPES = {
7569     'load' : Roo.form.Action.Load,
7570     'submit' : Roo.form.Action.Submit
7571 };/*
7572  * - LGPL
7573  *
7574  * form
7575  *
7576  */
7577
7578 /**
7579  * @class Roo.bootstrap.Form
7580  * @extends Roo.bootstrap.Component
7581  * Bootstrap Form class
7582  * @cfg {String} method  GET | POST (default POST)
7583  * @cfg {String} labelAlign top | left (default top)
7584  * @cfg {String} align left  | right - for navbars
7585  * @cfg {Boolean} loadMask load mask when submit (default true)
7586
7587  *
7588  * @constructor
7589  * Create a new Form
7590  * @param {Object} config The config object
7591  */
7592
7593
7594 Roo.bootstrap.Form = function(config){
7595     
7596     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7597     
7598     Roo.bootstrap.Form.popover.apply();
7599     
7600     this.addEvents({
7601         /**
7602          * @event clientvalidation
7603          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7604          * @param {Form} this
7605          * @param {Boolean} valid true if the form has passed client-side validation
7606          */
7607         clientvalidation: true,
7608         /**
7609          * @event beforeaction
7610          * Fires before any action is performed. Return false to cancel the action.
7611          * @param {Form} this
7612          * @param {Action} action The action to be performed
7613          */
7614         beforeaction: true,
7615         /**
7616          * @event actionfailed
7617          * Fires when an action fails.
7618          * @param {Form} this
7619          * @param {Action} action The action that failed
7620          */
7621         actionfailed : true,
7622         /**
7623          * @event actioncomplete
7624          * Fires when an action is completed.
7625          * @param {Form} this
7626          * @param {Action} action The action that completed
7627          */
7628         actioncomplete : true
7629     });
7630 };
7631
7632 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7633
7634      /**
7635      * @cfg {String} method
7636      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7637      */
7638     method : 'POST',
7639     /**
7640      * @cfg {String} url
7641      * The URL to use for form actions if one isn't supplied in the action options.
7642      */
7643     /**
7644      * @cfg {Boolean} fileUpload
7645      * Set to true if this form is a file upload.
7646      */
7647
7648     /**
7649      * @cfg {Object} baseParams
7650      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7651      */
7652
7653     /**
7654      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7655      */
7656     timeout: 30,
7657     /**
7658      * @cfg {Sting} align (left|right) for navbar forms
7659      */
7660     align : 'left',
7661
7662     // private
7663     activeAction : null,
7664
7665     /**
7666      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7667      * element by passing it or its id or mask the form itself by passing in true.
7668      * @type Mixed
7669      */
7670     waitMsgTarget : false,
7671
7672     loadMask : true,
7673     
7674     /**
7675      * @cfg {Boolean} errorMask (true|false) default false
7676      */
7677     errorMask : false,
7678     
7679     /**
7680      * @cfg {Number} maskOffset Default 100
7681      */
7682     maskOffset : 100,
7683     
7684     /**
7685      * @cfg {Boolean} maskBody
7686      */
7687     maskBody : false,
7688
7689     getAutoCreate : function(){
7690
7691         var cfg = {
7692             tag: 'form',
7693             method : this.method || 'POST',
7694             id : this.id || Roo.id(),
7695             cls : ''
7696         };
7697         if (this.parent().xtype.match(/^Nav/)) {
7698             cfg.cls = 'navbar-form navbar-' + this.align;
7699
7700         }
7701
7702         if (this.labelAlign == 'left' ) {
7703             cfg.cls += ' form-horizontal';
7704         }
7705
7706
7707         return cfg;
7708     },
7709     initEvents : function()
7710     {
7711         this.el.on('submit', this.onSubmit, this);
7712         // this was added as random key presses on the form where triggering form submit.
7713         this.el.on('keypress', function(e) {
7714             if (e.getCharCode() != 13) {
7715                 return true;
7716             }
7717             // we might need to allow it for textareas.. and some other items.
7718             // check e.getTarget().
7719
7720             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7721                 return true;
7722             }
7723
7724             Roo.log("keypress blocked");
7725
7726             e.preventDefault();
7727             return false;
7728         });
7729         
7730     },
7731     // private
7732     onSubmit : function(e){
7733         e.stopEvent();
7734     },
7735
7736      /**
7737      * Returns true if client-side validation on the form is successful.
7738      * @return Boolean
7739      */
7740     isValid : function(){
7741         var items = this.getItems();
7742         var valid = true;
7743         var target = false;
7744         
7745         items.each(function(f){
7746             if(f.validate()){
7747                 return;
7748             }
7749             valid = false;
7750
7751             if(!target && f.el.isVisible(true)){
7752                 target = f;
7753             }
7754            
7755         });
7756         
7757         if(this.errorMask && !valid){
7758             Roo.bootstrap.Form.popover.mask(this, target);
7759         }
7760         
7761         return valid;
7762     },
7763     
7764     /**
7765      * Returns true if any fields in this form have changed since their original load.
7766      * @return Boolean
7767      */
7768     isDirty : function(){
7769         var dirty = false;
7770         var items = this.getItems();
7771         items.each(function(f){
7772            if(f.isDirty()){
7773                dirty = true;
7774                return false;
7775            }
7776            return true;
7777         });
7778         return dirty;
7779     },
7780      /**
7781      * Performs a predefined action (submit or load) or custom actions you define on this form.
7782      * @param {String} actionName The name of the action type
7783      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7784      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7785      * accept other config options):
7786      * <pre>
7787 Property          Type             Description
7788 ----------------  ---------------  ----------------------------------------------------------------------------------
7789 url               String           The url for the action (defaults to the form's url)
7790 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7791 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7792 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7793                                    validate the form on the client (defaults to false)
7794      * </pre>
7795      * @return {BasicForm} this
7796      */
7797     doAction : function(action, options){
7798         if(typeof action == 'string'){
7799             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7800         }
7801         if(this.fireEvent('beforeaction', this, action) !== false){
7802             this.beforeAction(action);
7803             action.run.defer(100, action);
7804         }
7805         return this;
7806     },
7807
7808     // private
7809     beforeAction : function(action){
7810         var o = action.options;
7811         
7812         if(this.loadMask){
7813             
7814             if(this.maskBody){
7815                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7816             } else {
7817                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7818             }
7819         }
7820         // not really supported yet.. ??
7821
7822         //if(this.waitMsgTarget === true){
7823         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7824         //}else if(this.waitMsgTarget){
7825         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7826         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7827         //}else {
7828         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7829        // }
7830
7831     },
7832
7833     // private
7834     afterAction : function(action, success){
7835         this.activeAction = null;
7836         var o = action.options;
7837
7838         if(this.loadMask){
7839             
7840             if(this.maskBody){
7841                 Roo.get(document.body).unmask();
7842             } else {
7843                 this.el.unmask();
7844             }
7845         }
7846         
7847         //if(this.waitMsgTarget === true){
7848 //            this.el.unmask();
7849         //}else if(this.waitMsgTarget){
7850         //    this.waitMsgTarget.unmask();
7851         //}else{
7852         //    Roo.MessageBox.updateProgress(1);
7853         //    Roo.MessageBox.hide();
7854        // }
7855         //
7856         if(success){
7857             if(o.reset){
7858                 this.reset();
7859             }
7860             Roo.callback(o.success, o.scope, [this, action]);
7861             this.fireEvent('actioncomplete', this, action);
7862
7863         }else{
7864
7865             // failure condition..
7866             // we have a scenario where updates need confirming.
7867             // eg. if a locking scenario exists..
7868             // we look for { errors : { needs_confirm : true }} in the response.
7869             if (
7870                 (typeof(action.result) != 'undefined')  &&
7871                 (typeof(action.result.errors) != 'undefined')  &&
7872                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7873            ){
7874                 var _t = this;
7875                 Roo.log("not supported yet");
7876                  /*
7877
7878                 Roo.MessageBox.confirm(
7879                     "Change requires confirmation",
7880                     action.result.errorMsg,
7881                     function(r) {
7882                         if (r != 'yes') {
7883                             return;
7884                         }
7885                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7886                     }
7887
7888                 );
7889                 */
7890
7891
7892                 return;
7893             }
7894
7895             Roo.callback(o.failure, o.scope, [this, action]);
7896             // show an error message if no failed handler is set..
7897             if (!this.hasListener('actionfailed')) {
7898                 Roo.log("need to add dialog support");
7899                 /*
7900                 Roo.MessageBox.alert("Error",
7901                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7902                         action.result.errorMsg :
7903                         "Saving Failed, please check your entries or try again"
7904                 );
7905                 */
7906             }
7907
7908             this.fireEvent('actionfailed', this, action);
7909         }
7910
7911     },
7912     /**
7913      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7914      * @param {String} id The value to search for
7915      * @return Field
7916      */
7917     findField : function(id){
7918         var items = this.getItems();
7919         var field = items.get(id);
7920         if(!field){
7921              items.each(function(f){
7922                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7923                     field = f;
7924                     return false;
7925                 }
7926                 return true;
7927             });
7928         }
7929         return field || null;
7930     },
7931      /**
7932      * Mark fields in this form invalid in bulk.
7933      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7934      * @return {BasicForm} this
7935      */
7936     markInvalid : function(errors){
7937         if(errors instanceof Array){
7938             for(var i = 0, len = errors.length; i < len; i++){
7939                 var fieldError = errors[i];
7940                 var f = this.findField(fieldError.id);
7941                 if(f){
7942                     f.markInvalid(fieldError.msg);
7943                 }
7944             }
7945         }else{
7946             var field, id;
7947             for(id in errors){
7948                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7949                     field.markInvalid(errors[id]);
7950                 }
7951             }
7952         }
7953         //Roo.each(this.childForms || [], function (f) {
7954         //    f.markInvalid(errors);
7955         //});
7956
7957         return this;
7958     },
7959
7960     /**
7961      * Set values for fields in this form in bulk.
7962      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7963      * @return {BasicForm} this
7964      */
7965     setValues : function(values){
7966         if(values instanceof Array){ // array of objects
7967             for(var i = 0, len = values.length; i < len; i++){
7968                 var v = values[i];
7969                 var f = this.findField(v.id);
7970                 if(f){
7971                     f.setValue(v.value);
7972                     if(this.trackResetOnLoad){
7973                         f.originalValue = f.getValue();
7974                     }
7975                 }
7976             }
7977         }else{ // object hash
7978             var field, id;
7979             for(id in values){
7980                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7981
7982                     if (field.setFromData &&
7983                         field.valueField &&
7984                         field.displayField &&
7985                         // combos' with local stores can
7986                         // be queried via setValue()
7987                         // to set their value..
7988                         (field.store && !field.store.isLocal)
7989                         ) {
7990                         // it's a combo
7991                         var sd = { };
7992                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7993                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7994                         field.setFromData(sd);
7995
7996                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7997                         
7998                         field.setFromData(values);
7999                         
8000                     } else {
8001                         field.setValue(values[id]);
8002                     }
8003
8004
8005                     if(this.trackResetOnLoad){
8006                         field.originalValue = field.getValue();
8007                     }
8008                 }
8009             }
8010         }
8011
8012         //Roo.each(this.childForms || [], function (f) {
8013         //    f.setValues(values);
8014         //});
8015
8016         return this;
8017     },
8018
8019     /**
8020      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8021      * they are returned as an array.
8022      * @param {Boolean} asString
8023      * @return {Object}
8024      */
8025     getValues : function(asString){
8026         //if (this.childForms) {
8027             // copy values from the child forms
8028         //    Roo.each(this.childForms, function (f) {
8029         //        this.setValues(f.getValues());
8030         //    }, this);
8031         //}
8032
8033
8034
8035         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8036         if(asString === true){
8037             return fs;
8038         }
8039         return Roo.urlDecode(fs);
8040     },
8041
8042     /**
8043      * Returns the fields in this form as an object with key/value pairs.
8044      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8045      * @return {Object}
8046      */
8047     getFieldValues : function(with_hidden)
8048     {
8049         var items = this.getItems();
8050         var ret = {};
8051         items.each(function(f){
8052             
8053             if (!f.getName()) {
8054                 return;
8055             }
8056             
8057             var v = f.getValue();
8058             
8059             if (f.inputType =='radio') {
8060                 if (typeof(ret[f.getName()]) == 'undefined') {
8061                     ret[f.getName()] = ''; // empty..
8062                 }
8063
8064                 if (!f.el.dom.checked) {
8065                     return;
8066
8067                 }
8068                 v = f.el.dom.value;
8069
8070             }
8071             
8072             if(f.xtype == 'MoneyField'){
8073                 ret[f.currencyName] = f.getCurrency();
8074             }
8075
8076             // not sure if this supported any more..
8077             if ((typeof(v) == 'object') && f.getRawValue) {
8078                 v = f.getRawValue() ; // dates..
8079             }
8080             // combo boxes where name != hiddenName...
8081             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8082                 ret[f.name] = f.getRawValue();
8083             }
8084             ret[f.getName()] = v;
8085         });
8086
8087         return ret;
8088     },
8089
8090     /**
8091      * Clears all invalid messages in this form.
8092      * @return {BasicForm} this
8093      */
8094     clearInvalid : function(){
8095         var items = this.getItems();
8096
8097         items.each(function(f){
8098            f.clearInvalid();
8099         });
8100
8101         return this;
8102     },
8103
8104     /**
8105      * Resets this form.
8106      * @return {BasicForm} this
8107      */
8108     reset : function(){
8109         var items = this.getItems();
8110         items.each(function(f){
8111             f.reset();
8112         });
8113
8114         Roo.each(this.childForms || [], function (f) {
8115             f.reset();
8116         });
8117
8118
8119         return this;
8120     },
8121     
8122     getItems : function()
8123     {
8124         var r=new Roo.util.MixedCollection(false, function(o){
8125             return o.id || (o.id = Roo.id());
8126         });
8127         var iter = function(el) {
8128             if (el.inputEl) {
8129                 r.add(el);
8130             }
8131             if (!el.items) {
8132                 return;
8133             }
8134             Roo.each(el.items,function(e) {
8135                 iter(e);
8136             });
8137         };
8138
8139         iter(this);
8140         return r;
8141     }
8142
8143 });
8144
8145 Roo.apply(Roo.bootstrap.Form, {
8146     
8147     popover : {
8148         
8149         padding : 5,
8150         
8151         isApplied : false,
8152         
8153         isMasked : false,
8154         
8155         form : false,
8156         
8157         target : false,
8158         
8159         toolTip : false,
8160         
8161         intervalID : false,
8162         
8163         maskEl : false,
8164         
8165         apply : function()
8166         {
8167             if(this.isApplied){
8168                 return;
8169             }
8170             
8171             this.maskEl = {
8172                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8173                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8174                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8175                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8176             };
8177             
8178             this.maskEl.top.enableDisplayMode("block");
8179             this.maskEl.left.enableDisplayMode("block");
8180             this.maskEl.bottom.enableDisplayMode("block");
8181             this.maskEl.right.enableDisplayMode("block");
8182             
8183             this.toolTip = new Roo.bootstrap.Tooltip({
8184                 cls : 'roo-form-error-popover',
8185                 alignment : {
8186                     'left' : ['r-l', [-2,0], 'right'],
8187                     'right' : ['l-r', [2,0], 'left'],
8188                     'bottom' : ['tl-bl', [0,2], 'top'],
8189                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8190                 }
8191             });
8192             
8193             this.toolTip.render(Roo.get(document.body));
8194
8195             this.toolTip.el.enableDisplayMode("block");
8196             
8197             Roo.get(document.body).on('click', function(){
8198                 this.unmask();
8199             }, this);
8200             
8201             Roo.get(document.body).on('touchstart', function(){
8202                 this.unmask();
8203             }, this);
8204             
8205             this.isApplied = true
8206         },
8207         
8208         mask : function(form, target)
8209         {
8210             this.form = form;
8211             
8212             this.target = target;
8213             
8214             if(!this.form.errorMask || !target.el){
8215                 return;
8216             }
8217             
8218             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8219             
8220             Roo.log(scrollable);
8221             
8222             var ot = this.target.el.calcOffsetsTo(scrollable);
8223             
8224             var scrollTo = ot[1] - this.form.maskOffset;
8225             
8226             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8227             
8228             scrollable.scrollTo('top', scrollTo);
8229             
8230             var box = this.target.el.getBox();
8231             Roo.log(box);
8232             var zIndex = Roo.bootstrap.Modal.zIndex++;
8233
8234             
8235             this.maskEl.top.setStyle('position', 'absolute');
8236             this.maskEl.top.setStyle('z-index', zIndex);
8237             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8238             this.maskEl.top.setLeft(0);
8239             this.maskEl.top.setTop(0);
8240             this.maskEl.top.show();
8241             
8242             this.maskEl.left.setStyle('position', 'absolute');
8243             this.maskEl.left.setStyle('z-index', zIndex);
8244             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8245             this.maskEl.left.setLeft(0);
8246             this.maskEl.left.setTop(box.y - this.padding);
8247             this.maskEl.left.show();
8248
8249             this.maskEl.bottom.setStyle('position', 'absolute');
8250             this.maskEl.bottom.setStyle('z-index', zIndex);
8251             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8252             this.maskEl.bottom.setLeft(0);
8253             this.maskEl.bottom.setTop(box.bottom + this.padding);
8254             this.maskEl.bottom.show();
8255
8256             this.maskEl.right.setStyle('position', 'absolute');
8257             this.maskEl.right.setStyle('z-index', zIndex);
8258             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8259             this.maskEl.right.setLeft(box.right + this.padding);
8260             this.maskEl.right.setTop(box.y - this.padding);
8261             this.maskEl.right.show();
8262
8263             this.toolTip.bindEl = this.target.el;
8264
8265             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8266
8267             var tip = this.target.blankText;
8268
8269             if(this.target.getValue() !== '' ) {
8270                 
8271                 if (this.target.invalidText.length) {
8272                     tip = this.target.invalidText;
8273                 } else if (this.target.regexText.length){
8274                     tip = this.target.regexText;
8275                 }
8276             }
8277
8278             this.toolTip.show(tip);
8279
8280             this.intervalID = window.setInterval(function() {
8281                 Roo.bootstrap.Form.popover.unmask();
8282             }, 10000);
8283
8284             window.onwheel = function(){ return false;};
8285             
8286             (function(){ this.isMasked = true; }).defer(500, this);
8287             
8288         },
8289         
8290         unmask : function()
8291         {
8292             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8293                 return;
8294             }
8295             
8296             this.maskEl.top.setStyle('position', 'absolute');
8297             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8298             this.maskEl.top.hide();
8299
8300             this.maskEl.left.setStyle('position', 'absolute');
8301             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8302             this.maskEl.left.hide();
8303
8304             this.maskEl.bottom.setStyle('position', 'absolute');
8305             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8306             this.maskEl.bottom.hide();
8307
8308             this.maskEl.right.setStyle('position', 'absolute');
8309             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8310             this.maskEl.right.hide();
8311             
8312             this.toolTip.hide();
8313             
8314             this.toolTip.el.hide();
8315             
8316             window.onwheel = function(){ return true;};
8317             
8318             if(this.intervalID){
8319                 window.clearInterval(this.intervalID);
8320                 this.intervalID = false;
8321             }
8322             
8323             this.isMasked = false;
8324             
8325         }
8326         
8327     }
8328     
8329 });
8330
8331 /*
8332  * Based on:
8333  * Ext JS Library 1.1.1
8334  * Copyright(c) 2006-2007, Ext JS, LLC.
8335  *
8336  * Originally Released Under LGPL - original licence link has changed is not relivant.
8337  *
8338  * Fork - LGPL
8339  * <script type="text/javascript">
8340  */
8341 /**
8342  * @class Roo.form.VTypes
8343  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8344  * @singleton
8345  */
8346 Roo.form.VTypes = function(){
8347     // closure these in so they are only created once.
8348     var alpha = /^[a-zA-Z_]+$/;
8349     var alphanum = /^[a-zA-Z0-9_]+$/;
8350     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8351     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8352
8353     // All these messages and functions are configurable
8354     return {
8355         /**
8356          * The function used to validate email addresses
8357          * @param {String} value The email address
8358          */
8359         'email' : function(v){
8360             return email.test(v);
8361         },
8362         /**
8363          * The error text to display when the email validation function returns false
8364          * @type String
8365          */
8366         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8367         /**
8368          * The keystroke filter mask to be applied on email input
8369          * @type RegExp
8370          */
8371         'emailMask' : /[a-z0-9_\.\-@]/i,
8372
8373         /**
8374          * The function used to validate URLs
8375          * @param {String} value The URL
8376          */
8377         'url' : function(v){
8378             return url.test(v);
8379         },
8380         /**
8381          * The error text to display when the url validation function returns false
8382          * @type String
8383          */
8384         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8385         
8386         /**
8387          * The function used to validate alpha values
8388          * @param {String} value The value
8389          */
8390         'alpha' : function(v){
8391             return alpha.test(v);
8392         },
8393         /**
8394          * The error text to display when the alpha validation function returns false
8395          * @type String
8396          */
8397         'alphaText' : 'This field should only contain letters and _',
8398         /**
8399          * The keystroke filter mask to be applied on alpha input
8400          * @type RegExp
8401          */
8402         'alphaMask' : /[a-z_]/i,
8403
8404         /**
8405          * The function used to validate alphanumeric values
8406          * @param {String} value The value
8407          */
8408         'alphanum' : function(v){
8409             return alphanum.test(v);
8410         },
8411         /**
8412          * The error text to display when the alphanumeric validation function returns false
8413          * @type String
8414          */
8415         'alphanumText' : 'This field should only contain letters, numbers and _',
8416         /**
8417          * The keystroke filter mask to be applied on alphanumeric input
8418          * @type RegExp
8419          */
8420         'alphanumMask' : /[a-z0-9_]/i
8421     };
8422 }();/*
8423  * - LGPL
8424  *
8425  * Input
8426  * 
8427  */
8428
8429 /**
8430  * @class Roo.bootstrap.Input
8431  * @extends Roo.bootstrap.Component
8432  * Bootstrap Input class
8433  * @cfg {Boolean} disabled is it disabled
8434  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8435  * @cfg {String} name name of the input
8436  * @cfg {string} fieldLabel - the label associated
8437  * @cfg {string} placeholder - placeholder to put in text.
8438  * @cfg {string}  before - input group add on before
8439  * @cfg {string} after - input group add on after
8440  * @cfg {string} size - (lg|sm) or leave empty..
8441  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8442  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8443  * @cfg {Number} md colspan out of 12 for computer-sized screens
8444  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8445  * @cfg {string} value default value of the input
8446  * @cfg {Number} labelWidth set the width of label 
8447  * @cfg {Number} labellg set the width of label (1-12)
8448  * @cfg {Number} labelmd set the width of label (1-12)
8449  * @cfg {Number} labelsm set the width of label (1-12)
8450  * @cfg {Number} labelxs set the width of label (1-12)
8451  * @cfg {String} labelAlign (top|left)
8452  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8453  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8454  * @cfg {String} indicatorpos (left|right) default left
8455
8456  * @cfg {String} align (left|center|right) Default left
8457  * @cfg {Boolean} forceFeedback (true|false) Default false
8458  * 
8459  * 
8460  * 
8461  * 
8462  * @constructor
8463  * Create a new Input
8464  * @param {Object} config The config object
8465  */
8466
8467 Roo.bootstrap.Input = function(config){
8468     
8469     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8470     
8471     this.addEvents({
8472         /**
8473          * @event focus
8474          * Fires when this field receives input focus.
8475          * @param {Roo.form.Field} this
8476          */
8477         focus : true,
8478         /**
8479          * @event blur
8480          * Fires when this field loses input focus.
8481          * @param {Roo.form.Field} this
8482          */
8483         blur : true,
8484         /**
8485          * @event specialkey
8486          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8487          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8488          * @param {Roo.form.Field} this
8489          * @param {Roo.EventObject} e The event object
8490          */
8491         specialkey : true,
8492         /**
8493          * @event change
8494          * Fires just before the field blurs if the field value has changed.
8495          * @param {Roo.form.Field} this
8496          * @param {Mixed} newValue The new value
8497          * @param {Mixed} oldValue The original value
8498          */
8499         change : true,
8500         /**
8501          * @event invalid
8502          * Fires after the field has been marked as invalid.
8503          * @param {Roo.form.Field} this
8504          * @param {String} msg The validation message
8505          */
8506         invalid : true,
8507         /**
8508          * @event valid
8509          * Fires after the field has been validated with no errors.
8510          * @param {Roo.form.Field} this
8511          */
8512         valid : true,
8513          /**
8514          * @event keyup
8515          * Fires after the key up
8516          * @param {Roo.form.Field} this
8517          * @param {Roo.EventObject}  e The event Object
8518          */
8519         keyup : true
8520     });
8521 };
8522
8523 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8524      /**
8525      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8526       automatic validation (defaults to "keyup").
8527      */
8528     validationEvent : "keyup",
8529      /**
8530      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8531      */
8532     validateOnBlur : true,
8533     /**
8534      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8535      */
8536     validationDelay : 250,
8537      /**
8538      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8539      */
8540     focusClass : "x-form-focus",  // not needed???
8541     
8542        
8543     /**
8544      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8545      */
8546     invalidClass : "has-warning",
8547     
8548     /**
8549      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8550      */
8551     validClass : "has-success",
8552     
8553     /**
8554      * @cfg {Boolean} hasFeedback (true|false) default true
8555      */
8556     hasFeedback : true,
8557     
8558     /**
8559      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8560      */
8561     invalidFeedbackClass : "glyphicon-warning-sign",
8562     
8563     /**
8564      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8565      */
8566     validFeedbackClass : "glyphicon-ok",
8567     
8568     /**
8569      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8570      */
8571     selectOnFocus : false,
8572     
8573      /**
8574      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8575      */
8576     maskRe : null,
8577        /**
8578      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8579      */
8580     vtype : null,
8581     
8582       /**
8583      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8584      */
8585     disableKeyFilter : false,
8586     
8587        /**
8588      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8589      */
8590     disabled : false,
8591      /**
8592      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8593      */
8594     allowBlank : true,
8595     /**
8596      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8597      */
8598     blankText : "Please complete this mandatory field",
8599     
8600      /**
8601      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8602      */
8603     minLength : 0,
8604     /**
8605      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8606      */
8607     maxLength : Number.MAX_VALUE,
8608     /**
8609      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8610      */
8611     minLengthText : "The minimum length for this field is {0}",
8612     /**
8613      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8614      */
8615     maxLengthText : "The maximum length for this field is {0}",
8616   
8617     
8618     /**
8619      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8620      * If available, this function will be called only after the basic validators all return true, and will be passed the
8621      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8622      */
8623     validator : null,
8624     /**
8625      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8626      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8627      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8628      */
8629     regex : null,
8630     /**
8631      * @cfg {String} regexText -- Depricated - use Invalid Text
8632      */
8633     regexText : "",
8634     
8635     /**
8636      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8637      */
8638     invalidText : "",
8639     
8640     
8641     
8642     autocomplete: false,
8643     
8644     
8645     fieldLabel : '',
8646     inputType : 'text',
8647     
8648     name : false,
8649     placeholder: false,
8650     before : false,
8651     after : false,
8652     size : false,
8653     hasFocus : false,
8654     preventMark: false,
8655     isFormField : true,
8656     value : '',
8657     labelWidth : 2,
8658     labelAlign : false,
8659     readOnly : false,
8660     align : false,
8661     formatedValue : false,
8662     forceFeedback : false,
8663     
8664     indicatorpos : 'left',
8665     
8666     labellg : 0,
8667     labelmd : 0,
8668     labelsm : 0,
8669     labelxs : 0,
8670     
8671     parentLabelAlign : function()
8672     {
8673         var parent = this;
8674         while (parent.parent()) {
8675             parent = parent.parent();
8676             if (typeof(parent.labelAlign) !='undefined') {
8677                 return parent.labelAlign;
8678             }
8679         }
8680         return 'left';
8681         
8682     },
8683     
8684     getAutoCreate : function()
8685     {
8686         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8687         
8688         var id = Roo.id();
8689         
8690         var cfg = {};
8691         
8692         if(this.inputType != 'hidden'){
8693             cfg.cls = 'form-group' //input-group
8694         }
8695         
8696         var input =  {
8697             tag: 'input',
8698             id : id,
8699             type : this.inputType,
8700             value : this.value,
8701             cls : 'form-control',
8702             placeholder : this.placeholder || '',
8703             autocomplete : this.autocomplete || 'new-password'
8704         };
8705         
8706         if(this.align){
8707             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8708         }
8709         
8710         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8711             input.maxLength = this.maxLength;
8712         }
8713         
8714         if (this.disabled) {
8715             input.disabled=true;
8716         }
8717         
8718         if (this.readOnly) {
8719             input.readonly=true;
8720         }
8721         
8722         if (this.name) {
8723             input.name = this.name;
8724         }
8725         
8726         if (this.size) {
8727             input.cls += ' input-' + this.size;
8728         }
8729         
8730         var settings=this;
8731         ['xs','sm','md','lg'].map(function(size){
8732             if (settings[size]) {
8733                 cfg.cls += ' col-' + size + '-' + settings[size];
8734             }
8735         });
8736         
8737         var inputblock = input;
8738         
8739         var feedback = {
8740             tag: 'span',
8741             cls: 'glyphicon form-control-feedback'
8742         };
8743             
8744         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8745             
8746             inputblock = {
8747                 cls : 'has-feedback',
8748                 cn :  [
8749                     input,
8750                     feedback
8751                 ] 
8752             };  
8753         }
8754         
8755         if (this.before || this.after) {
8756             
8757             inputblock = {
8758                 cls : 'input-group',
8759                 cn :  [] 
8760             };
8761             
8762             if (this.before && typeof(this.before) == 'string') {
8763                 
8764                 inputblock.cn.push({
8765                     tag :'span',
8766                     cls : 'roo-input-before input-group-addon',
8767                     html : this.before
8768                 });
8769             }
8770             if (this.before && typeof(this.before) == 'object') {
8771                 this.before = Roo.factory(this.before);
8772                 
8773                 inputblock.cn.push({
8774                     tag :'span',
8775                     cls : 'roo-input-before input-group-' +
8776                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8777                 });
8778             }
8779             
8780             inputblock.cn.push(input);
8781             
8782             if (this.after && typeof(this.after) == 'string') {
8783                 inputblock.cn.push({
8784                     tag :'span',
8785                     cls : 'roo-input-after input-group-addon',
8786                     html : this.after
8787                 });
8788             }
8789             if (this.after && typeof(this.after) == 'object') {
8790                 this.after = Roo.factory(this.after);
8791                 
8792                 inputblock.cn.push({
8793                     tag :'span',
8794                     cls : 'roo-input-after input-group-' +
8795                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8796                 });
8797             }
8798             
8799             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8800                 inputblock.cls += ' has-feedback';
8801                 inputblock.cn.push(feedback);
8802             }
8803         };
8804         
8805         if (align ==='left' && this.fieldLabel.length) {
8806             
8807             cfg.cls += ' roo-form-group-label-left';
8808             
8809             cfg.cn = [
8810                 {
8811                     tag : 'i',
8812                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8813                     tooltip : 'This field is required'
8814                 },
8815                 {
8816                     tag: 'label',
8817                     'for' :  id,
8818                     cls : 'control-label',
8819                     html : this.fieldLabel
8820
8821                 },
8822                 {
8823                     cls : "", 
8824                     cn: [
8825                         inputblock
8826                     ]
8827                 }
8828             ];
8829             
8830             var labelCfg = cfg.cn[1];
8831             var contentCfg = cfg.cn[2];
8832             
8833             if(this.indicatorpos == 'right'){
8834                 cfg.cn = [
8835                     {
8836                         tag: 'label',
8837                         'for' :  id,
8838                         cls : 'control-label',
8839                         cn : [
8840                             {
8841                                 tag : 'span',
8842                                 html : this.fieldLabel
8843                             },
8844                             {
8845                                 tag : 'i',
8846                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8847                                 tooltip : 'This field is required'
8848                             }
8849                         ]
8850                     },
8851                     {
8852                         cls : "",
8853                         cn: [
8854                             inputblock
8855                         ]
8856                     }
8857
8858                 ];
8859                 
8860                 labelCfg = cfg.cn[0];
8861                 contentCfg = cfg.cn[1];
8862             
8863             }
8864             
8865             if(this.labelWidth > 12){
8866                 labelCfg.style = "width: " + this.labelWidth + 'px';
8867             }
8868             
8869             if(this.labelWidth < 13 && this.labelmd == 0){
8870                 this.labelmd = this.labelWidth;
8871             }
8872             
8873             if(this.labellg > 0){
8874                 labelCfg.cls += ' col-lg-' + this.labellg;
8875                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8876             }
8877             
8878             if(this.labelmd > 0){
8879                 labelCfg.cls += ' col-md-' + this.labelmd;
8880                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8881             }
8882             
8883             if(this.labelsm > 0){
8884                 labelCfg.cls += ' col-sm-' + this.labelsm;
8885                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8886             }
8887             
8888             if(this.labelxs > 0){
8889                 labelCfg.cls += ' col-xs-' + this.labelxs;
8890                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8891             }
8892             
8893             
8894         } else if ( this.fieldLabel.length) {
8895                 
8896             cfg.cn = [
8897                 {
8898                     tag : 'i',
8899                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8900                     tooltip : 'This field is required'
8901                 },
8902                 {
8903                     tag: 'label',
8904                    //cls : 'input-group-addon',
8905                     html : this.fieldLabel
8906
8907                 },
8908
8909                inputblock
8910
8911            ];
8912            
8913            if(this.indicatorpos == 'right'){
8914                 
8915                 cfg.cn = [
8916                     {
8917                         tag: 'label',
8918                        //cls : 'input-group-addon',
8919                         html : this.fieldLabel
8920
8921                     },
8922                     {
8923                         tag : 'i',
8924                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8925                         tooltip : 'This field is required'
8926                     },
8927
8928                    inputblock
8929
8930                ];
8931
8932             }
8933
8934         } else {
8935             
8936             cfg.cn = [
8937
8938                     inputblock
8939
8940             ];
8941                 
8942                 
8943         };
8944         
8945         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8946            cfg.cls += ' navbar-form';
8947         }
8948         
8949         if (this.parentType === 'NavGroup') {
8950            cfg.cls += ' navbar-form';
8951            cfg.tag = 'li';
8952         }
8953         
8954         return cfg;
8955         
8956     },
8957     /**
8958      * return the real input element.
8959      */
8960     inputEl: function ()
8961     {
8962         return this.el.select('input.form-control',true).first();
8963     },
8964     
8965     tooltipEl : function()
8966     {
8967         return this.inputEl();
8968     },
8969     
8970     indicatorEl : function()
8971     {
8972         var indicator = this.el.select('i.roo-required-indicator',true).first();
8973         
8974         if(!indicator){
8975             return false;
8976         }
8977         
8978         return indicator;
8979         
8980     },
8981     
8982     setDisabled : function(v)
8983     {
8984         var i  = this.inputEl().dom;
8985         if (!v) {
8986             i.removeAttribute('disabled');
8987             return;
8988             
8989         }
8990         i.setAttribute('disabled','true');
8991     },
8992     initEvents : function()
8993     {
8994           
8995         this.inputEl().on("keydown" , this.fireKey,  this);
8996         this.inputEl().on("focus", this.onFocus,  this);
8997         this.inputEl().on("blur", this.onBlur,  this);
8998         
8999         this.inputEl().relayEvent('keyup', this);
9000         
9001         this.indicator = this.indicatorEl();
9002         
9003         if(this.indicator){
9004             this.indicator.addClass('invisible');
9005             
9006         }
9007  
9008         // reference to original value for reset
9009         this.originalValue = this.getValue();
9010         //Roo.form.TextField.superclass.initEvents.call(this);
9011         if(this.validationEvent == 'keyup'){
9012             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9013             this.inputEl().on('keyup', this.filterValidation, this);
9014         }
9015         else if(this.validationEvent !== false){
9016             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9017         }
9018         
9019         if(this.selectOnFocus){
9020             this.on("focus", this.preFocus, this);
9021             
9022         }
9023         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9024             this.inputEl().on("keypress", this.filterKeys, this);
9025         } else {
9026             this.inputEl().relayEvent('keypress', this);
9027         }
9028        /* if(this.grow){
9029             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9030             this.el.on("click", this.autoSize,  this);
9031         }
9032         */
9033         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9034             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9035         }
9036         
9037         if (typeof(this.before) == 'object') {
9038             this.before.render(this.el.select('.roo-input-before',true).first());
9039         }
9040         if (typeof(this.after) == 'object') {
9041             this.after.render(this.el.select('.roo-input-after',true).first());
9042         }
9043         
9044         
9045     },
9046     filterValidation : function(e){
9047         if(!e.isNavKeyPress()){
9048             this.validationTask.delay(this.validationDelay);
9049         }
9050     },
9051      /**
9052      * Validates the field value
9053      * @return {Boolean} True if the value is valid, else false
9054      */
9055     validate : function(){
9056         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9057         if(this.disabled || this.validateValue(this.getRawValue())){
9058             this.markValid();
9059             return true;
9060         }
9061         
9062         this.markInvalid();
9063         return false;
9064     },
9065     
9066     
9067     /**
9068      * Validates a value according to the field's validation rules and marks the field as invalid
9069      * if the validation fails
9070      * @param {Mixed} value The value to validate
9071      * @return {Boolean} True if the value is valid, else false
9072      */
9073     validateValue : function(value){
9074         if(value.length < 1)  { // if it's blank
9075             if(this.allowBlank){
9076                 return true;
9077             }            
9078             return this.inputEl().hasClass('hide') ? true : false;
9079         }
9080         
9081         if(value.length < this.minLength){
9082             return false;
9083         }
9084         if(value.length > this.maxLength){
9085             return false;
9086         }
9087         if(this.vtype){
9088             var vt = Roo.form.VTypes;
9089             if(!vt[this.vtype](value, this)){
9090                 return false;
9091             }
9092         }
9093         if(typeof this.validator == "function"){
9094             var msg = this.validator(value);
9095             if(msg !== true){
9096                 return false;
9097             }
9098             if (typeof(msg) == 'string') {
9099                 this.invalidText = msg;
9100             }
9101         }
9102         
9103         if(this.regex && !this.regex.test(value)){
9104             return false;
9105         }
9106         
9107         return true;
9108     },
9109
9110     
9111     
9112      // private
9113     fireKey : function(e){
9114         //Roo.log('field ' + e.getKey());
9115         if(e.isNavKeyPress()){
9116             this.fireEvent("specialkey", this, e);
9117         }
9118     },
9119     focus : function (selectText){
9120         if(this.rendered){
9121             this.inputEl().focus();
9122             if(selectText === true){
9123                 this.inputEl().dom.select();
9124             }
9125         }
9126         return this;
9127     } ,
9128     
9129     onFocus : function(){
9130         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9131            // this.el.addClass(this.focusClass);
9132         }
9133         if(!this.hasFocus){
9134             this.hasFocus = true;
9135             this.startValue = this.getValue();
9136             this.fireEvent("focus", this);
9137         }
9138     },
9139     
9140     beforeBlur : Roo.emptyFn,
9141
9142     
9143     // private
9144     onBlur : function(){
9145         this.beforeBlur();
9146         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9147             //this.el.removeClass(this.focusClass);
9148         }
9149         this.hasFocus = false;
9150         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9151             this.validate();
9152         }
9153         var v = this.getValue();
9154         if(String(v) !== String(this.startValue)){
9155             this.fireEvent('change', this, v, this.startValue);
9156         }
9157         this.fireEvent("blur", this);
9158     },
9159     
9160     /**
9161      * Resets the current field value to the originally loaded value and clears any validation messages
9162      */
9163     reset : function(){
9164         this.setValue(this.originalValue);
9165         this.validate();
9166     },
9167      /**
9168      * Returns the name of the field
9169      * @return {Mixed} name The name field
9170      */
9171     getName: function(){
9172         return this.name;
9173     },
9174      /**
9175      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9176      * @return {Mixed} value The field value
9177      */
9178     getValue : function(){
9179         
9180         var v = this.inputEl().getValue();
9181         
9182         return v;
9183     },
9184     /**
9185      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9186      * @return {Mixed} value The field value
9187      */
9188     getRawValue : function(){
9189         var v = this.inputEl().getValue();
9190         
9191         return v;
9192     },
9193     
9194     /**
9195      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9196      * @param {Mixed} value The value to set
9197      */
9198     setRawValue : function(v){
9199         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9200     },
9201     
9202     selectText : function(start, end){
9203         var v = this.getRawValue();
9204         if(v.length > 0){
9205             start = start === undefined ? 0 : start;
9206             end = end === undefined ? v.length : end;
9207             var d = this.inputEl().dom;
9208             if(d.setSelectionRange){
9209                 d.setSelectionRange(start, end);
9210             }else if(d.createTextRange){
9211                 var range = d.createTextRange();
9212                 range.moveStart("character", start);
9213                 range.moveEnd("character", v.length-end);
9214                 range.select();
9215             }
9216         }
9217     },
9218     
9219     /**
9220      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9221      * @param {Mixed} value The value to set
9222      */
9223     setValue : function(v){
9224         this.value = v;
9225         if(this.rendered){
9226             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9227             this.validate();
9228         }
9229     },
9230     
9231     /*
9232     processValue : function(value){
9233         if(this.stripCharsRe){
9234             var newValue = value.replace(this.stripCharsRe, '');
9235             if(newValue !== value){
9236                 this.setRawValue(newValue);
9237                 return newValue;
9238             }
9239         }
9240         return value;
9241     },
9242   */
9243     preFocus : function(){
9244         
9245         if(this.selectOnFocus){
9246             this.inputEl().dom.select();
9247         }
9248     },
9249     filterKeys : function(e){
9250         var k = e.getKey();
9251         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9252             return;
9253         }
9254         var c = e.getCharCode(), cc = String.fromCharCode(c);
9255         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9256             return;
9257         }
9258         if(!this.maskRe.test(cc)){
9259             e.stopEvent();
9260         }
9261     },
9262      /**
9263      * Clear any invalid styles/messages for this field
9264      */
9265     clearInvalid : function(){
9266         
9267         if(!this.el || this.preventMark){ // not rendered
9268             return;
9269         }
9270         
9271      
9272         this.el.removeClass(this.invalidClass);
9273         
9274         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9275             
9276             var feedback = this.el.select('.form-control-feedback', true).first();
9277             
9278             if(feedback){
9279                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9280             }
9281             
9282         }
9283         
9284         this.fireEvent('valid', this);
9285     },
9286     
9287      /**
9288      * Mark this field as valid
9289      */
9290     markValid : function()
9291     {
9292         if(!this.el  || this.preventMark){ // not rendered...
9293             return;
9294         }
9295         
9296         this.el.removeClass([this.invalidClass, this.validClass]);
9297         
9298         var feedback = this.el.select('.form-control-feedback', true).first();
9299             
9300         if(feedback){
9301             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9302         }
9303         
9304         if(this.indicator){
9305             this.indicator.removeClass('visible');
9306             this.indicator.addClass('invisible');
9307         }
9308         
9309         if(this.disabled){
9310             return;
9311         }
9312         
9313         if(this.allowBlank && !this.getRawValue().length){
9314             return;
9315         }
9316         
9317         this.el.addClass(this.validClass);
9318         
9319         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9320             
9321             var feedback = this.el.select('.form-control-feedback', true).first();
9322             
9323             if(feedback){
9324                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9325                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9326             }
9327             
9328         }
9329         
9330         this.fireEvent('valid', this);
9331     },
9332     
9333      /**
9334      * Mark this field as invalid
9335      * @param {String} msg The validation message
9336      */
9337     markInvalid : function(msg)
9338     {
9339         if(!this.el  || this.preventMark){ // not rendered
9340             return;
9341         }
9342         
9343         this.el.removeClass([this.invalidClass, this.validClass]);
9344         
9345         var feedback = this.el.select('.form-control-feedback', true).first();
9346             
9347         if(feedback){
9348             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9349         }
9350
9351         if(this.disabled){
9352             return;
9353         }
9354         
9355         if(this.allowBlank && !this.getRawValue().length){
9356             return;
9357         }
9358         
9359         if(this.indicator){
9360             this.indicator.removeClass('invisible');
9361             this.indicator.addClass('visible');
9362         }
9363         
9364         this.el.addClass(this.invalidClass);
9365         
9366         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9367             
9368             var feedback = this.el.select('.form-control-feedback', true).first();
9369             
9370             if(feedback){
9371                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9372                 
9373                 if(this.getValue().length || this.forceFeedback){
9374                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9375                 }
9376                 
9377             }
9378             
9379         }
9380         
9381         this.fireEvent('invalid', this, msg);
9382     },
9383     // private
9384     SafariOnKeyDown : function(event)
9385     {
9386         // this is a workaround for a password hang bug on chrome/ webkit.
9387         if (this.inputEl().dom.type != 'password') {
9388             return;
9389         }
9390         
9391         var isSelectAll = false;
9392         
9393         if(this.inputEl().dom.selectionEnd > 0){
9394             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9395         }
9396         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9397             event.preventDefault();
9398             this.setValue('');
9399             return;
9400         }
9401         
9402         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9403             
9404             event.preventDefault();
9405             // this is very hacky as keydown always get's upper case.
9406             //
9407             var cc = String.fromCharCode(event.getCharCode());
9408             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9409             
9410         }
9411     },
9412     adjustWidth : function(tag, w){
9413         tag = tag.toLowerCase();
9414         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9415             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9416                 if(tag == 'input'){
9417                     return w + 2;
9418                 }
9419                 if(tag == 'textarea'){
9420                     return w-2;
9421                 }
9422             }else if(Roo.isOpera){
9423                 if(tag == 'input'){
9424                     return w + 2;
9425                 }
9426                 if(tag == 'textarea'){
9427                     return w-2;
9428                 }
9429             }
9430         }
9431         return w;
9432     },
9433     
9434     setFieldLabel : function(v)
9435     {
9436         if(!this.rendered){
9437             return;
9438         }
9439         
9440         if(this.indicator){
9441             var ar = this.el.select('label > span',true);
9442             
9443             if (ar.elements.length) {
9444                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9445                 this.fieldLabel = v;
9446                 return;
9447             }
9448             
9449             var br = this.el.select('label',true);
9450             
9451             if(br.elements.length) {
9452                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9453                 this.fieldLabel = v;
9454                 return;
9455             }
9456             
9457             Roo.log('Cannot Found any of label > span || label in input');
9458             return;
9459         }
9460         
9461         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9462         this.fieldLabel = v;
9463         
9464         
9465     }
9466 });
9467
9468  
9469 /*
9470  * - LGPL
9471  *
9472  * Input
9473  * 
9474  */
9475
9476 /**
9477  * @class Roo.bootstrap.TextArea
9478  * @extends Roo.bootstrap.Input
9479  * Bootstrap TextArea class
9480  * @cfg {Number} cols Specifies the visible width of a text area
9481  * @cfg {Number} rows Specifies the visible number of lines in a text area
9482  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9483  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9484  * @cfg {string} html text
9485  * 
9486  * @constructor
9487  * Create a new TextArea
9488  * @param {Object} config The config object
9489  */
9490
9491 Roo.bootstrap.TextArea = function(config){
9492     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9493    
9494 };
9495
9496 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9497      
9498     cols : false,
9499     rows : 5,
9500     readOnly : false,
9501     warp : 'soft',
9502     resize : false,
9503     value: false,
9504     html: false,
9505     
9506     getAutoCreate : function(){
9507         
9508         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9509         
9510         var id = Roo.id();
9511         
9512         var cfg = {};
9513         
9514         if(this.inputType != 'hidden'){
9515             cfg.cls = 'form-group' //input-group
9516         }
9517         
9518         var input =  {
9519             tag: 'textarea',
9520             id : id,
9521             warp : this.warp,
9522             rows : this.rows,
9523             value : this.value || '',
9524             html: this.html || '',
9525             cls : 'form-control',
9526             placeholder : this.placeholder || '' 
9527             
9528         };
9529         
9530         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9531             input.maxLength = this.maxLength;
9532         }
9533         
9534         if(this.resize){
9535             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9536         }
9537         
9538         if(this.cols){
9539             input.cols = this.cols;
9540         }
9541         
9542         if (this.readOnly) {
9543             input.readonly = true;
9544         }
9545         
9546         if (this.name) {
9547             input.name = this.name;
9548         }
9549         
9550         if (this.size) {
9551             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9552         }
9553         
9554         var settings=this;
9555         ['xs','sm','md','lg'].map(function(size){
9556             if (settings[size]) {
9557                 cfg.cls += ' col-' + size + '-' + settings[size];
9558             }
9559         });
9560         
9561         var inputblock = input;
9562         
9563         if(this.hasFeedback && !this.allowBlank){
9564             
9565             var feedback = {
9566                 tag: 'span',
9567                 cls: 'glyphicon form-control-feedback'
9568             };
9569
9570             inputblock = {
9571                 cls : 'has-feedback',
9572                 cn :  [
9573                     input,
9574                     feedback
9575                 ] 
9576             };  
9577         }
9578         
9579         
9580         if (this.before || this.after) {
9581             
9582             inputblock = {
9583                 cls : 'input-group',
9584                 cn :  [] 
9585             };
9586             if (this.before) {
9587                 inputblock.cn.push({
9588                     tag :'span',
9589                     cls : 'input-group-addon',
9590                     html : this.before
9591                 });
9592             }
9593             
9594             inputblock.cn.push(input);
9595             
9596             if(this.hasFeedback && !this.allowBlank){
9597                 inputblock.cls += ' has-feedback';
9598                 inputblock.cn.push(feedback);
9599             }
9600             
9601             if (this.after) {
9602                 inputblock.cn.push({
9603                     tag :'span',
9604                     cls : 'input-group-addon',
9605                     html : this.after
9606                 });
9607             }
9608             
9609         }
9610         
9611         if (align ==='left' && this.fieldLabel.length) {
9612             cfg.cn = [
9613                 {
9614                     tag: 'label',
9615                     'for' :  id,
9616                     cls : 'control-label',
9617                     html : this.fieldLabel
9618                 },
9619                 {
9620                     cls : "",
9621                     cn: [
9622                         inputblock
9623                     ]
9624                 }
9625
9626             ];
9627             
9628             if(this.labelWidth > 12){
9629                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9630             }
9631
9632             if(this.labelWidth < 13 && this.labelmd == 0){
9633                 this.labelmd = this.labelWidth;
9634             }
9635
9636             if(this.labellg > 0){
9637                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9638                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9639             }
9640
9641             if(this.labelmd > 0){
9642                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9643                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9644             }
9645
9646             if(this.labelsm > 0){
9647                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9648                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9649             }
9650
9651             if(this.labelxs > 0){
9652                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9653                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9654             }
9655             
9656         } else if ( this.fieldLabel.length) {
9657             cfg.cn = [
9658
9659                {
9660                    tag: 'label',
9661                    //cls : 'input-group-addon',
9662                    html : this.fieldLabel
9663
9664                },
9665
9666                inputblock
9667
9668            ];
9669
9670         } else {
9671
9672             cfg.cn = [
9673
9674                 inputblock
9675
9676             ];
9677                 
9678         }
9679         
9680         if (this.disabled) {
9681             input.disabled=true;
9682         }
9683         
9684         return cfg;
9685         
9686     },
9687     /**
9688      * return the real textarea element.
9689      */
9690     inputEl: function ()
9691     {
9692         return this.el.select('textarea.form-control',true).first();
9693     },
9694     
9695     /**
9696      * Clear any invalid styles/messages for this field
9697      */
9698     clearInvalid : function()
9699     {
9700         
9701         if(!this.el || this.preventMark){ // not rendered
9702             return;
9703         }
9704         
9705         var label = this.el.select('label', true).first();
9706         var icon = this.el.select('i.fa-star', true).first();
9707         
9708         if(label && icon){
9709             icon.remove();
9710         }
9711         
9712         this.el.removeClass(this.invalidClass);
9713         
9714         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9715             
9716             var feedback = this.el.select('.form-control-feedback', true).first();
9717             
9718             if(feedback){
9719                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9720             }
9721             
9722         }
9723         
9724         this.fireEvent('valid', this);
9725     },
9726     
9727      /**
9728      * Mark this field as valid
9729      */
9730     markValid : function()
9731     {
9732         if(!this.el  || this.preventMark){ // not rendered
9733             return;
9734         }
9735         
9736         this.el.removeClass([this.invalidClass, this.validClass]);
9737         
9738         var feedback = this.el.select('.form-control-feedback', true).first();
9739             
9740         if(feedback){
9741             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9742         }
9743
9744         if(this.disabled || this.allowBlank){
9745             return;
9746         }
9747         
9748         var label = this.el.select('label', true).first();
9749         var icon = this.el.select('i.fa-star', true).first();
9750         
9751         if(label && icon){
9752             icon.remove();
9753         }
9754         
9755         this.el.addClass(this.validClass);
9756         
9757         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9758             
9759             var feedback = this.el.select('.form-control-feedback', true).first();
9760             
9761             if(feedback){
9762                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9763                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9764             }
9765             
9766         }
9767         
9768         this.fireEvent('valid', this);
9769     },
9770     
9771      /**
9772      * Mark this field as invalid
9773      * @param {String} msg The validation message
9774      */
9775     markInvalid : function(msg)
9776     {
9777         if(!this.el  || this.preventMark){ // not rendered
9778             return;
9779         }
9780         
9781         this.el.removeClass([this.invalidClass, this.validClass]);
9782         
9783         var feedback = this.el.select('.form-control-feedback', true).first();
9784             
9785         if(feedback){
9786             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9787         }
9788
9789         if(this.disabled || this.allowBlank){
9790             return;
9791         }
9792         
9793         var label = this.el.select('label', true).first();
9794         var icon = this.el.select('i.fa-star', true).first();
9795         
9796         if(!this.getValue().length && label && !icon){
9797             this.el.createChild({
9798                 tag : 'i',
9799                 cls : 'text-danger fa fa-lg fa-star',
9800                 tooltip : 'This field is required',
9801                 style : 'margin-right:5px;'
9802             }, label, true);
9803         }
9804
9805         this.el.addClass(this.invalidClass);
9806         
9807         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9808             
9809             var feedback = this.el.select('.form-control-feedback', true).first();
9810             
9811             if(feedback){
9812                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9813                 
9814                 if(this.getValue().length || this.forceFeedback){
9815                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9816                 }
9817                 
9818             }
9819             
9820         }
9821         
9822         this.fireEvent('invalid', this, msg);
9823     }
9824 });
9825
9826  
9827 /*
9828  * - LGPL
9829  *
9830  * trigger field - base class for combo..
9831  * 
9832  */
9833  
9834 /**
9835  * @class Roo.bootstrap.TriggerField
9836  * @extends Roo.bootstrap.Input
9837  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9838  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9839  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9840  * for which you can provide a custom implementation.  For example:
9841  * <pre><code>
9842 var trigger = new Roo.bootstrap.TriggerField();
9843 trigger.onTriggerClick = myTriggerFn;
9844 trigger.applyTo('my-field');
9845 </code></pre>
9846  *
9847  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9848  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9849  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9850  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9851  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9852
9853  * @constructor
9854  * Create a new TriggerField.
9855  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9856  * to the base TextField)
9857  */
9858 Roo.bootstrap.TriggerField = function(config){
9859     this.mimicing = false;
9860     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9861 };
9862
9863 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9864     /**
9865      * @cfg {String} triggerClass A CSS class to apply to the trigger
9866      */
9867      /**
9868      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9869      */
9870     hideTrigger:false,
9871
9872     /**
9873      * @cfg {Boolean} removable (true|false) special filter default false
9874      */
9875     removable : false,
9876     
9877     /** @cfg {Boolean} grow @hide */
9878     /** @cfg {Number} growMin @hide */
9879     /** @cfg {Number} growMax @hide */
9880
9881     /**
9882      * @hide 
9883      * @method
9884      */
9885     autoSize: Roo.emptyFn,
9886     // private
9887     monitorTab : true,
9888     // private
9889     deferHeight : true,
9890
9891     
9892     actionMode : 'wrap',
9893     
9894     caret : false,
9895     
9896     
9897     getAutoCreate : function(){
9898        
9899         var align = this.labelAlign || this.parentLabelAlign();
9900         
9901         var id = Roo.id();
9902         
9903         var cfg = {
9904             cls: 'form-group' //input-group
9905         };
9906         
9907         
9908         var input =  {
9909             tag: 'input',
9910             id : id,
9911             type : this.inputType,
9912             cls : 'form-control',
9913             autocomplete: 'new-password',
9914             placeholder : this.placeholder || '' 
9915             
9916         };
9917         if (this.name) {
9918             input.name = this.name;
9919         }
9920         if (this.size) {
9921             input.cls += ' input-' + this.size;
9922         }
9923         
9924         if (this.disabled) {
9925             input.disabled=true;
9926         }
9927         
9928         var inputblock = input;
9929         
9930         if(this.hasFeedback && !this.allowBlank){
9931             
9932             var feedback = {
9933                 tag: 'span',
9934                 cls: 'glyphicon form-control-feedback'
9935             };
9936             
9937             if(this.removable && !this.editable && !this.tickable){
9938                 inputblock = {
9939                     cls : 'has-feedback',
9940                     cn :  [
9941                         inputblock,
9942                         {
9943                             tag: 'button',
9944                             html : 'x',
9945                             cls : 'roo-combo-removable-btn close'
9946                         },
9947                         feedback
9948                     ] 
9949                 };
9950             } else {
9951                 inputblock = {
9952                     cls : 'has-feedback',
9953                     cn :  [
9954                         inputblock,
9955                         feedback
9956                     ] 
9957                 };
9958             }
9959
9960         } else {
9961             if(this.removable && !this.editable && !this.tickable){
9962                 inputblock = {
9963                     cls : 'roo-removable',
9964                     cn :  [
9965                         inputblock,
9966                         {
9967                             tag: 'button',
9968                             html : 'x',
9969                             cls : 'roo-combo-removable-btn close'
9970                         }
9971                     ] 
9972                 };
9973             }
9974         }
9975         
9976         if (this.before || this.after) {
9977             
9978             inputblock = {
9979                 cls : 'input-group',
9980                 cn :  [] 
9981             };
9982             if (this.before) {
9983                 inputblock.cn.push({
9984                     tag :'span',
9985                     cls : 'input-group-addon',
9986                     html : this.before
9987                 });
9988             }
9989             
9990             inputblock.cn.push(input);
9991             
9992             if(this.hasFeedback && !this.allowBlank){
9993                 inputblock.cls += ' has-feedback';
9994                 inputblock.cn.push(feedback);
9995             }
9996             
9997             if (this.after) {
9998                 inputblock.cn.push({
9999                     tag :'span',
10000                     cls : 'input-group-addon',
10001                     html : this.after
10002                 });
10003             }
10004             
10005         };
10006         
10007         var box = {
10008             tag: 'div',
10009             cn: [
10010                 {
10011                     tag: 'input',
10012                     type : 'hidden',
10013                     cls: 'form-hidden-field'
10014                 },
10015                 inputblock
10016             ]
10017             
10018         };
10019         
10020         if(this.multiple){
10021             box = {
10022                 tag: 'div',
10023                 cn: [
10024                     {
10025                         tag: 'input',
10026                         type : 'hidden',
10027                         cls: 'form-hidden-field'
10028                     },
10029                     {
10030                         tag: 'ul',
10031                         cls: 'roo-select2-choices',
10032                         cn:[
10033                             {
10034                                 tag: 'li',
10035                                 cls: 'roo-select2-search-field',
10036                                 cn: [
10037
10038                                     inputblock
10039                                 ]
10040                             }
10041                         ]
10042                     }
10043                 ]
10044             }
10045         };
10046         
10047         var combobox = {
10048             cls: 'roo-select2-container input-group',
10049             cn: [
10050                 box
10051 //                {
10052 //                    tag: 'ul',
10053 //                    cls: 'typeahead typeahead-long dropdown-menu',
10054 //                    style: 'display:none'
10055 //                }
10056             ]
10057         };
10058         
10059         if(!this.multiple && this.showToggleBtn){
10060             
10061             var caret = {
10062                         tag: 'span',
10063                         cls: 'caret'
10064              };
10065             if (this.caret != false) {
10066                 caret = {
10067                      tag: 'i',
10068                      cls: 'fa fa-' + this.caret
10069                 };
10070                 
10071             }
10072             
10073             combobox.cn.push({
10074                 tag :'span',
10075                 cls : 'input-group-addon btn dropdown-toggle',
10076                 cn : [
10077                     caret,
10078                     {
10079                         tag: 'span',
10080                         cls: 'combobox-clear',
10081                         cn  : [
10082                             {
10083                                 tag : 'i',
10084                                 cls: 'icon-remove'
10085                             }
10086                         ]
10087                     }
10088                 ]
10089
10090             })
10091         }
10092         
10093         if(this.multiple){
10094             combobox.cls += ' roo-select2-container-multi';
10095         }
10096         
10097         if (align ==='left' && this.fieldLabel.length) {
10098             
10099             cfg.cls += ' roo-form-group-label-left';
10100
10101             cfg.cn = [
10102                 {
10103                     tag : 'i',
10104                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10105                     tooltip : 'This field is required'
10106                 },
10107                 {
10108                     tag: 'label',
10109                     'for' :  id,
10110                     cls : 'control-label',
10111                     html : this.fieldLabel
10112
10113                 },
10114                 {
10115                     cls : "", 
10116                     cn: [
10117                         combobox
10118                     ]
10119                 }
10120
10121             ];
10122             
10123             var labelCfg = cfg.cn[1];
10124             var contentCfg = cfg.cn[2];
10125             
10126             if(this.indicatorpos == 'right'){
10127                 cfg.cn = [
10128                     {
10129                         tag: 'label',
10130                         'for' :  id,
10131                         cls : 'control-label',
10132                         cn : [
10133                             {
10134                                 tag : 'span',
10135                                 html : this.fieldLabel
10136                             },
10137                             {
10138                                 tag : 'i',
10139                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10140                                 tooltip : 'This field is required'
10141                             }
10142                         ]
10143                     },
10144                     {
10145                         cls : "", 
10146                         cn: [
10147                             combobox
10148                         ]
10149                     }
10150
10151                 ];
10152                 
10153                 labelCfg = cfg.cn[0];
10154                 contentCfg = cfg.cn[1];
10155             }
10156             
10157             if(this.labelWidth > 12){
10158                 labelCfg.style = "width: " + this.labelWidth + 'px';
10159             }
10160             
10161             if(this.labelWidth < 13 && this.labelmd == 0){
10162                 this.labelmd = this.labelWidth;
10163             }
10164             
10165             if(this.labellg > 0){
10166                 labelCfg.cls += ' col-lg-' + this.labellg;
10167                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10168             }
10169             
10170             if(this.labelmd > 0){
10171                 labelCfg.cls += ' col-md-' + this.labelmd;
10172                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10173             }
10174             
10175             if(this.labelsm > 0){
10176                 labelCfg.cls += ' col-sm-' + this.labelsm;
10177                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10178             }
10179             
10180             if(this.labelxs > 0){
10181                 labelCfg.cls += ' col-xs-' + this.labelxs;
10182                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10183             }
10184             
10185         } else if ( this.fieldLabel.length) {
10186 //                Roo.log(" label");
10187             cfg.cn = [
10188                 {
10189                    tag : 'i',
10190                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10191                    tooltip : 'This field is required'
10192                },
10193                {
10194                    tag: 'label',
10195                    //cls : 'input-group-addon',
10196                    html : this.fieldLabel
10197
10198                },
10199
10200                combobox
10201
10202             ];
10203             
10204             if(this.indicatorpos == 'right'){
10205                 
10206                 cfg.cn = [
10207                     {
10208                        tag: 'label',
10209                        cn : [
10210                            {
10211                                tag : 'span',
10212                                html : this.fieldLabel
10213                            },
10214                            {
10215                               tag : 'i',
10216                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10217                               tooltip : 'This field is required'
10218                            }
10219                        ]
10220
10221                     },
10222                     combobox
10223
10224                 ];
10225
10226             }
10227
10228         } else {
10229             
10230 //                Roo.log(" no label && no align");
10231                 cfg = combobox
10232                      
10233                 
10234         }
10235         
10236         var settings=this;
10237         ['xs','sm','md','lg'].map(function(size){
10238             if (settings[size]) {
10239                 cfg.cls += ' col-' + size + '-' + settings[size];
10240             }
10241         });
10242         
10243         return cfg;
10244         
10245     },
10246     
10247     
10248     
10249     // private
10250     onResize : function(w, h){
10251 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10252 //        if(typeof w == 'number'){
10253 //            var x = w - this.trigger.getWidth();
10254 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10255 //            this.trigger.setStyle('left', x+'px');
10256 //        }
10257     },
10258
10259     // private
10260     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10261
10262     // private
10263     getResizeEl : function(){
10264         return this.inputEl();
10265     },
10266
10267     // private
10268     getPositionEl : function(){
10269         return this.inputEl();
10270     },
10271
10272     // private
10273     alignErrorIcon : function(){
10274         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10275     },
10276
10277     // private
10278     initEvents : function(){
10279         
10280         this.createList();
10281         
10282         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10283         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10284         if(!this.multiple && this.showToggleBtn){
10285             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10286             if(this.hideTrigger){
10287                 this.trigger.setDisplayed(false);
10288             }
10289             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10290         }
10291         
10292         if(this.multiple){
10293             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10294         }
10295         
10296         if(this.removable && !this.editable && !this.tickable){
10297             var close = this.closeTriggerEl();
10298             
10299             if(close){
10300                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10301                 close.on('click', this.removeBtnClick, this, close);
10302             }
10303         }
10304         
10305         //this.trigger.addClassOnOver('x-form-trigger-over');
10306         //this.trigger.addClassOnClick('x-form-trigger-click');
10307         
10308         //if(!this.width){
10309         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10310         //}
10311     },
10312     
10313     closeTriggerEl : function()
10314     {
10315         var close = this.el.select('.roo-combo-removable-btn', true).first();
10316         return close ? close : false;
10317     },
10318     
10319     removeBtnClick : function(e, h, el)
10320     {
10321         e.preventDefault();
10322         
10323         if(this.fireEvent("remove", this) !== false){
10324             this.reset();
10325             this.fireEvent("afterremove", this)
10326         }
10327     },
10328     
10329     createList : function()
10330     {
10331         this.list = Roo.get(document.body).createChild({
10332             tag: 'ul',
10333             cls: 'typeahead typeahead-long dropdown-menu',
10334             style: 'display:none'
10335         });
10336         
10337         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10338         
10339     },
10340
10341     // private
10342     initTrigger : function(){
10343        
10344     },
10345
10346     // private
10347     onDestroy : function(){
10348         if(this.trigger){
10349             this.trigger.removeAllListeners();
10350           //  this.trigger.remove();
10351         }
10352         //if(this.wrap){
10353         //    this.wrap.remove();
10354         //}
10355         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10356     },
10357
10358     // private
10359     onFocus : function(){
10360         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10361         /*
10362         if(!this.mimicing){
10363             this.wrap.addClass('x-trigger-wrap-focus');
10364             this.mimicing = true;
10365             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10366             if(this.monitorTab){
10367                 this.el.on("keydown", this.checkTab, this);
10368             }
10369         }
10370         */
10371     },
10372
10373     // private
10374     checkTab : function(e){
10375         if(e.getKey() == e.TAB){
10376             this.triggerBlur();
10377         }
10378     },
10379
10380     // private
10381     onBlur : function(){
10382         // do nothing
10383     },
10384
10385     // private
10386     mimicBlur : function(e, t){
10387         /*
10388         if(!this.wrap.contains(t) && this.validateBlur()){
10389             this.triggerBlur();
10390         }
10391         */
10392     },
10393
10394     // private
10395     triggerBlur : function(){
10396         this.mimicing = false;
10397         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10398         if(this.monitorTab){
10399             this.el.un("keydown", this.checkTab, this);
10400         }
10401         //this.wrap.removeClass('x-trigger-wrap-focus');
10402         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10403     },
10404
10405     // private
10406     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10407     validateBlur : function(e, t){
10408         return true;
10409     },
10410
10411     // private
10412     onDisable : function(){
10413         this.inputEl().dom.disabled = true;
10414         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10415         //if(this.wrap){
10416         //    this.wrap.addClass('x-item-disabled');
10417         //}
10418     },
10419
10420     // private
10421     onEnable : function(){
10422         this.inputEl().dom.disabled = false;
10423         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10424         //if(this.wrap){
10425         //    this.el.removeClass('x-item-disabled');
10426         //}
10427     },
10428
10429     // private
10430     onShow : function(){
10431         var ae = this.getActionEl();
10432         
10433         if(ae){
10434             ae.dom.style.display = '';
10435             ae.dom.style.visibility = 'visible';
10436         }
10437     },
10438
10439     // private
10440     
10441     onHide : function(){
10442         var ae = this.getActionEl();
10443         ae.dom.style.display = 'none';
10444     },
10445
10446     /**
10447      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10448      * by an implementing function.
10449      * @method
10450      * @param {EventObject} e
10451      */
10452     onTriggerClick : Roo.emptyFn
10453 });
10454  /*
10455  * Based on:
10456  * Ext JS Library 1.1.1
10457  * Copyright(c) 2006-2007, Ext JS, LLC.
10458  *
10459  * Originally Released Under LGPL - original licence link has changed is not relivant.
10460  *
10461  * Fork - LGPL
10462  * <script type="text/javascript">
10463  */
10464
10465
10466 /**
10467  * @class Roo.data.SortTypes
10468  * @singleton
10469  * Defines the default sorting (casting?) comparison functions used when sorting data.
10470  */
10471 Roo.data.SortTypes = {
10472     /**
10473      * Default sort that does nothing
10474      * @param {Mixed} s The value being converted
10475      * @return {Mixed} The comparison value
10476      */
10477     none : function(s){
10478         return s;
10479     },
10480     
10481     /**
10482      * The regular expression used to strip tags
10483      * @type {RegExp}
10484      * @property
10485      */
10486     stripTagsRE : /<\/?[^>]+>/gi,
10487     
10488     /**
10489      * Strips all HTML tags to sort on text only
10490      * @param {Mixed} s The value being converted
10491      * @return {String} The comparison value
10492      */
10493     asText : function(s){
10494         return String(s).replace(this.stripTagsRE, "");
10495     },
10496     
10497     /**
10498      * Strips all HTML tags to sort on text only - Case insensitive
10499      * @param {Mixed} s The value being converted
10500      * @return {String} The comparison value
10501      */
10502     asUCText : function(s){
10503         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10504     },
10505     
10506     /**
10507      * Case insensitive string
10508      * @param {Mixed} s The value being converted
10509      * @return {String} The comparison value
10510      */
10511     asUCString : function(s) {
10512         return String(s).toUpperCase();
10513     },
10514     
10515     /**
10516      * Date sorting
10517      * @param {Mixed} s The value being converted
10518      * @return {Number} The comparison value
10519      */
10520     asDate : function(s) {
10521         if(!s){
10522             return 0;
10523         }
10524         if(s instanceof Date){
10525             return s.getTime();
10526         }
10527         return Date.parse(String(s));
10528     },
10529     
10530     /**
10531      * Float sorting
10532      * @param {Mixed} s The value being converted
10533      * @return {Float} The comparison value
10534      */
10535     asFloat : function(s) {
10536         var val = parseFloat(String(s).replace(/,/g, ""));
10537         if(isNaN(val)) {
10538             val = 0;
10539         }
10540         return val;
10541     },
10542     
10543     /**
10544      * Integer sorting
10545      * @param {Mixed} s The value being converted
10546      * @return {Number} The comparison value
10547      */
10548     asInt : function(s) {
10549         var val = parseInt(String(s).replace(/,/g, ""));
10550         if(isNaN(val)) {
10551             val = 0;
10552         }
10553         return val;
10554     }
10555 };/*
10556  * Based on:
10557  * Ext JS Library 1.1.1
10558  * Copyright(c) 2006-2007, Ext JS, LLC.
10559  *
10560  * Originally Released Under LGPL - original licence link has changed is not relivant.
10561  *
10562  * Fork - LGPL
10563  * <script type="text/javascript">
10564  */
10565
10566 /**
10567 * @class Roo.data.Record
10568  * Instances of this class encapsulate both record <em>definition</em> information, and record
10569  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10570  * to access Records cached in an {@link Roo.data.Store} object.<br>
10571  * <p>
10572  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10573  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10574  * objects.<br>
10575  * <p>
10576  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10577  * @constructor
10578  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10579  * {@link #create}. The parameters are the same.
10580  * @param {Array} data An associative Array of data values keyed by the field name.
10581  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10582  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10583  * not specified an integer id is generated.
10584  */
10585 Roo.data.Record = function(data, id){
10586     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10587     this.data = data;
10588 };
10589
10590 /**
10591  * Generate a constructor for a specific record layout.
10592  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10593  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10594  * Each field definition object may contain the following properties: <ul>
10595  * <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,
10596  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10597  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10598  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10599  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10600  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10601  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10602  * this may be omitted.</p></li>
10603  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10604  * <ul><li>auto (Default, implies no conversion)</li>
10605  * <li>string</li>
10606  * <li>int</li>
10607  * <li>float</li>
10608  * <li>boolean</li>
10609  * <li>date</li></ul></p></li>
10610  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10611  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10612  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10613  * by the Reader into an object that will be stored in the Record. It is passed the
10614  * following parameters:<ul>
10615  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10616  * </ul></p></li>
10617  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10618  * </ul>
10619  * <br>usage:<br><pre><code>
10620 var TopicRecord = Roo.data.Record.create(
10621     {name: 'title', mapping: 'topic_title'},
10622     {name: 'author', mapping: 'username'},
10623     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10624     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10625     {name: 'lastPoster', mapping: 'user2'},
10626     {name: 'excerpt', mapping: 'post_text'}
10627 );
10628
10629 var myNewRecord = new TopicRecord({
10630     title: 'Do my job please',
10631     author: 'noobie',
10632     totalPosts: 1,
10633     lastPost: new Date(),
10634     lastPoster: 'Animal',
10635     excerpt: 'No way dude!'
10636 });
10637 myStore.add(myNewRecord);
10638 </code></pre>
10639  * @method create
10640  * @static
10641  */
10642 Roo.data.Record.create = function(o){
10643     var f = function(){
10644         f.superclass.constructor.apply(this, arguments);
10645     };
10646     Roo.extend(f, Roo.data.Record);
10647     var p = f.prototype;
10648     p.fields = new Roo.util.MixedCollection(false, function(field){
10649         return field.name;
10650     });
10651     for(var i = 0, len = o.length; i < len; i++){
10652         p.fields.add(new Roo.data.Field(o[i]));
10653     }
10654     f.getField = function(name){
10655         return p.fields.get(name);  
10656     };
10657     return f;
10658 };
10659
10660 Roo.data.Record.AUTO_ID = 1000;
10661 Roo.data.Record.EDIT = 'edit';
10662 Roo.data.Record.REJECT = 'reject';
10663 Roo.data.Record.COMMIT = 'commit';
10664
10665 Roo.data.Record.prototype = {
10666     /**
10667      * Readonly flag - true if this record has been modified.
10668      * @type Boolean
10669      */
10670     dirty : false,
10671     editing : false,
10672     error: null,
10673     modified: null,
10674
10675     // private
10676     join : function(store){
10677         this.store = store;
10678     },
10679
10680     /**
10681      * Set the named field to the specified value.
10682      * @param {String} name The name of the field to set.
10683      * @param {Object} value The value to set the field to.
10684      */
10685     set : function(name, value){
10686         if(this.data[name] == value){
10687             return;
10688         }
10689         this.dirty = true;
10690         if(!this.modified){
10691             this.modified = {};
10692         }
10693         if(typeof this.modified[name] == 'undefined'){
10694             this.modified[name] = this.data[name];
10695         }
10696         this.data[name] = value;
10697         if(!this.editing && this.store){
10698             this.store.afterEdit(this);
10699         }       
10700     },
10701
10702     /**
10703      * Get the value of the named field.
10704      * @param {String} name The name of the field to get the value of.
10705      * @return {Object} The value of the field.
10706      */
10707     get : function(name){
10708         return this.data[name]; 
10709     },
10710
10711     // private
10712     beginEdit : function(){
10713         this.editing = true;
10714         this.modified = {}; 
10715     },
10716
10717     // private
10718     cancelEdit : function(){
10719         this.editing = false;
10720         delete this.modified;
10721     },
10722
10723     // private
10724     endEdit : function(){
10725         this.editing = false;
10726         if(this.dirty && this.store){
10727             this.store.afterEdit(this);
10728         }
10729     },
10730
10731     /**
10732      * Usually called by the {@link Roo.data.Store} which owns the Record.
10733      * Rejects all changes made to the Record since either creation, or the last commit operation.
10734      * Modified fields are reverted to their original values.
10735      * <p>
10736      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10737      * of reject operations.
10738      */
10739     reject : function(){
10740         var m = this.modified;
10741         for(var n in m){
10742             if(typeof m[n] != "function"){
10743                 this.data[n] = m[n];
10744             }
10745         }
10746         this.dirty = false;
10747         delete this.modified;
10748         this.editing = false;
10749         if(this.store){
10750             this.store.afterReject(this);
10751         }
10752     },
10753
10754     /**
10755      * Usually called by the {@link Roo.data.Store} which owns the Record.
10756      * Commits all changes made to the Record since either creation, or the last commit operation.
10757      * <p>
10758      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10759      * of commit operations.
10760      */
10761     commit : function(){
10762         this.dirty = false;
10763         delete this.modified;
10764         this.editing = false;
10765         if(this.store){
10766             this.store.afterCommit(this);
10767         }
10768     },
10769
10770     // private
10771     hasError : function(){
10772         return this.error != null;
10773     },
10774
10775     // private
10776     clearError : function(){
10777         this.error = null;
10778     },
10779
10780     /**
10781      * Creates a copy of this record.
10782      * @param {String} id (optional) A new record id if you don't want to use this record's id
10783      * @return {Record}
10784      */
10785     copy : function(newId) {
10786         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10787     }
10788 };/*
10789  * Based on:
10790  * Ext JS Library 1.1.1
10791  * Copyright(c) 2006-2007, Ext JS, LLC.
10792  *
10793  * Originally Released Under LGPL - original licence link has changed is not relivant.
10794  *
10795  * Fork - LGPL
10796  * <script type="text/javascript">
10797  */
10798
10799
10800
10801 /**
10802  * @class Roo.data.Store
10803  * @extends Roo.util.Observable
10804  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10805  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10806  * <p>
10807  * 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
10808  * has no knowledge of the format of the data returned by the Proxy.<br>
10809  * <p>
10810  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10811  * instances from the data object. These records are cached and made available through accessor functions.
10812  * @constructor
10813  * Creates a new Store.
10814  * @param {Object} config A config object containing the objects needed for the Store to access data,
10815  * and read the data into Records.
10816  */
10817 Roo.data.Store = function(config){
10818     this.data = new Roo.util.MixedCollection(false);
10819     this.data.getKey = function(o){
10820         return o.id;
10821     };
10822     this.baseParams = {};
10823     // private
10824     this.paramNames = {
10825         "start" : "start",
10826         "limit" : "limit",
10827         "sort" : "sort",
10828         "dir" : "dir",
10829         "multisort" : "_multisort"
10830     };
10831
10832     if(config && config.data){
10833         this.inlineData = config.data;
10834         delete config.data;
10835     }
10836
10837     Roo.apply(this, config);
10838     
10839     if(this.reader){ // reader passed
10840         this.reader = Roo.factory(this.reader, Roo.data);
10841         this.reader.xmodule = this.xmodule || false;
10842         if(!this.recordType){
10843             this.recordType = this.reader.recordType;
10844         }
10845         if(this.reader.onMetaChange){
10846             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10847         }
10848     }
10849
10850     if(this.recordType){
10851         this.fields = this.recordType.prototype.fields;
10852     }
10853     this.modified = [];
10854
10855     this.addEvents({
10856         /**
10857          * @event datachanged
10858          * Fires when the data cache has changed, and a widget which is using this Store
10859          * as a Record cache should refresh its view.
10860          * @param {Store} this
10861          */
10862         datachanged : true,
10863         /**
10864          * @event metachange
10865          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10866          * @param {Store} this
10867          * @param {Object} meta The JSON metadata
10868          */
10869         metachange : true,
10870         /**
10871          * @event add
10872          * Fires when Records have been added to the Store
10873          * @param {Store} this
10874          * @param {Roo.data.Record[]} records The array of Records added
10875          * @param {Number} index The index at which the record(s) were added
10876          */
10877         add : true,
10878         /**
10879          * @event remove
10880          * Fires when a Record has been removed from the Store
10881          * @param {Store} this
10882          * @param {Roo.data.Record} record The Record that was removed
10883          * @param {Number} index The index at which the record was removed
10884          */
10885         remove : true,
10886         /**
10887          * @event update
10888          * Fires when a Record has been updated
10889          * @param {Store} this
10890          * @param {Roo.data.Record} record The Record that was updated
10891          * @param {String} operation The update operation being performed.  Value may be one of:
10892          * <pre><code>
10893  Roo.data.Record.EDIT
10894  Roo.data.Record.REJECT
10895  Roo.data.Record.COMMIT
10896          * </code></pre>
10897          */
10898         update : true,
10899         /**
10900          * @event clear
10901          * Fires when the data cache has been cleared.
10902          * @param {Store} this
10903          */
10904         clear : true,
10905         /**
10906          * @event beforeload
10907          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10908          * the load action will be canceled.
10909          * @param {Store} this
10910          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10911          */
10912         beforeload : true,
10913         /**
10914          * @event beforeloadadd
10915          * Fires after a new set of Records has been loaded.
10916          * @param {Store} this
10917          * @param {Roo.data.Record[]} records The Records that were loaded
10918          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10919          */
10920         beforeloadadd : true,
10921         /**
10922          * @event load
10923          * Fires after a new set of Records has been loaded, before they are added to the store.
10924          * @param {Store} this
10925          * @param {Roo.data.Record[]} records The Records that were loaded
10926          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10927          * @params {Object} return from reader
10928          */
10929         load : true,
10930         /**
10931          * @event loadexception
10932          * Fires if an exception occurs in the Proxy during loading.
10933          * Called with the signature of the Proxy's "loadexception" event.
10934          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10935          * 
10936          * @param {Proxy} 
10937          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10938          * @param {Object} load options 
10939          * @param {Object} jsonData from your request (normally this contains the Exception)
10940          */
10941         loadexception : true
10942     });
10943     
10944     if(this.proxy){
10945         this.proxy = Roo.factory(this.proxy, Roo.data);
10946         this.proxy.xmodule = this.xmodule || false;
10947         this.relayEvents(this.proxy,  ["loadexception"]);
10948     }
10949     this.sortToggle = {};
10950     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10951
10952     Roo.data.Store.superclass.constructor.call(this);
10953
10954     if(this.inlineData){
10955         this.loadData(this.inlineData);
10956         delete this.inlineData;
10957     }
10958 };
10959
10960 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10961      /**
10962     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10963     * without a remote query - used by combo/forms at present.
10964     */
10965     
10966     /**
10967     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10968     */
10969     /**
10970     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10971     */
10972     /**
10973     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10974     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10975     */
10976     /**
10977     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10978     * on any HTTP request
10979     */
10980     /**
10981     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10982     */
10983     /**
10984     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10985     */
10986     multiSort: false,
10987     /**
10988     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10989     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10990     */
10991     remoteSort : false,
10992
10993     /**
10994     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10995      * loaded or when a record is removed. (defaults to false).
10996     */
10997     pruneModifiedRecords : false,
10998
10999     // private
11000     lastOptions : null,
11001
11002     /**
11003      * Add Records to the Store and fires the add event.
11004      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11005      */
11006     add : function(records){
11007         records = [].concat(records);
11008         for(var i = 0, len = records.length; i < len; i++){
11009             records[i].join(this);
11010         }
11011         var index = this.data.length;
11012         this.data.addAll(records);
11013         this.fireEvent("add", this, records, index);
11014     },
11015
11016     /**
11017      * Remove a Record from the Store and fires the remove event.
11018      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11019      */
11020     remove : function(record){
11021         var index = this.data.indexOf(record);
11022         this.data.removeAt(index);
11023         if(this.pruneModifiedRecords){
11024             this.modified.remove(record);
11025         }
11026         this.fireEvent("remove", this, record, index);
11027     },
11028
11029     /**
11030      * Remove all Records from the Store and fires the clear event.
11031      */
11032     removeAll : function(){
11033         this.data.clear();
11034         if(this.pruneModifiedRecords){
11035             this.modified = [];
11036         }
11037         this.fireEvent("clear", this);
11038     },
11039
11040     /**
11041      * Inserts Records to the Store at the given index and fires the add event.
11042      * @param {Number} index The start index at which to insert the passed Records.
11043      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11044      */
11045     insert : function(index, records){
11046         records = [].concat(records);
11047         for(var i = 0, len = records.length; i < len; i++){
11048             this.data.insert(index, records[i]);
11049             records[i].join(this);
11050         }
11051         this.fireEvent("add", this, records, index);
11052     },
11053
11054     /**
11055      * Get the index within the cache of the passed Record.
11056      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11057      * @return {Number} The index of the passed Record. Returns -1 if not found.
11058      */
11059     indexOf : function(record){
11060         return this.data.indexOf(record);
11061     },
11062
11063     /**
11064      * Get the index within the cache of the Record with the passed id.
11065      * @param {String} id The id of the Record to find.
11066      * @return {Number} The index of the Record. Returns -1 if not found.
11067      */
11068     indexOfId : function(id){
11069         return this.data.indexOfKey(id);
11070     },
11071
11072     /**
11073      * Get the Record with the specified id.
11074      * @param {String} id The id of the Record to find.
11075      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11076      */
11077     getById : function(id){
11078         return this.data.key(id);
11079     },
11080
11081     /**
11082      * Get the Record at the specified index.
11083      * @param {Number} index The index of the Record to find.
11084      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11085      */
11086     getAt : function(index){
11087         return this.data.itemAt(index);
11088     },
11089
11090     /**
11091      * Returns a range of Records between specified indices.
11092      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11093      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11094      * @return {Roo.data.Record[]} An array of Records
11095      */
11096     getRange : function(start, end){
11097         return this.data.getRange(start, end);
11098     },
11099
11100     // private
11101     storeOptions : function(o){
11102         o = Roo.apply({}, o);
11103         delete o.callback;
11104         delete o.scope;
11105         this.lastOptions = o;
11106     },
11107
11108     /**
11109      * Loads the Record cache from the configured Proxy using the configured Reader.
11110      * <p>
11111      * If using remote paging, then the first load call must specify the <em>start</em>
11112      * and <em>limit</em> properties in the options.params property to establish the initial
11113      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11114      * <p>
11115      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11116      * and this call will return before the new data has been loaded. Perform any post-processing
11117      * in a callback function, or in a "load" event handler.</strong>
11118      * <p>
11119      * @param {Object} options An object containing properties which control loading options:<ul>
11120      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11121      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11122      * passed the following arguments:<ul>
11123      * <li>r : Roo.data.Record[]</li>
11124      * <li>options: Options object from the load call</li>
11125      * <li>success: Boolean success indicator</li></ul></li>
11126      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11127      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11128      * </ul>
11129      */
11130     load : function(options){
11131         options = options || {};
11132         if(this.fireEvent("beforeload", this, options) !== false){
11133             this.storeOptions(options);
11134             var p = Roo.apply(options.params || {}, this.baseParams);
11135             // if meta was not loaded from remote source.. try requesting it.
11136             if (!this.reader.metaFromRemote) {
11137                 p._requestMeta = 1;
11138             }
11139             if(this.sortInfo && this.remoteSort){
11140                 var pn = this.paramNames;
11141                 p[pn["sort"]] = this.sortInfo.field;
11142                 p[pn["dir"]] = this.sortInfo.direction;
11143             }
11144             if (this.multiSort) {
11145                 var pn = this.paramNames;
11146                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11147             }
11148             
11149             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11150         }
11151     },
11152
11153     /**
11154      * Reloads the Record cache from the configured Proxy using the configured Reader and
11155      * the options from the last load operation performed.
11156      * @param {Object} options (optional) An object containing properties which may override the options
11157      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11158      * the most recently used options are reused).
11159      */
11160     reload : function(options){
11161         this.load(Roo.applyIf(options||{}, this.lastOptions));
11162     },
11163
11164     // private
11165     // Called as a callback by the Reader during a load operation.
11166     loadRecords : function(o, options, success){
11167         if(!o || success === false){
11168             if(success !== false){
11169                 this.fireEvent("load", this, [], options, o);
11170             }
11171             if(options.callback){
11172                 options.callback.call(options.scope || this, [], options, false);
11173             }
11174             return;
11175         }
11176         // if data returned failure - throw an exception.
11177         if (o.success === false) {
11178             // show a message if no listener is registered.
11179             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11180                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11181             }
11182             // loadmask wil be hooked into this..
11183             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11184             return;
11185         }
11186         var r = o.records, t = o.totalRecords || r.length;
11187         
11188         this.fireEvent("beforeloadadd", this, r, options, o);
11189         
11190         if(!options || options.add !== true){
11191             if(this.pruneModifiedRecords){
11192                 this.modified = [];
11193             }
11194             for(var i = 0, len = r.length; i < len; i++){
11195                 r[i].join(this);
11196             }
11197             if(this.snapshot){
11198                 this.data = this.snapshot;
11199                 delete this.snapshot;
11200             }
11201             this.data.clear();
11202             this.data.addAll(r);
11203             this.totalLength = t;
11204             this.applySort();
11205             this.fireEvent("datachanged", this);
11206         }else{
11207             this.totalLength = Math.max(t, this.data.length+r.length);
11208             this.add(r);
11209         }
11210         
11211         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11212                 
11213             var e = new Roo.data.Record({});
11214
11215             e.set(this.parent.displayField, this.parent.emptyTitle);
11216             e.set(this.parent.valueField, '');
11217
11218             this.insert(0, e);
11219         }
11220             
11221         this.fireEvent("load", this, r, options, o);
11222         if(options.callback){
11223             options.callback.call(options.scope || this, r, options, true);
11224         }
11225     },
11226
11227
11228     /**
11229      * Loads data from a passed data block. A Reader which understands the format of the data
11230      * must have been configured in the constructor.
11231      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11232      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11233      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11234      */
11235     loadData : function(o, append){
11236         var r = this.reader.readRecords(o);
11237         this.loadRecords(r, {add: append}, true);
11238     },
11239
11240     /**
11241      * Gets the number of cached records.
11242      * <p>
11243      * <em>If using paging, this may not be the total size of the dataset. If the data object
11244      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11245      * the data set size</em>
11246      */
11247     getCount : function(){
11248         return this.data.length || 0;
11249     },
11250
11251     /**
11252      * Gets the total number of records in the dataset as returned by the server.
11253      * <p>
11254      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11255      * the dataset size</em>
11256      */
11257     getTotalCount : function(){
11258         return this.totalLength || 0;
11259     },
11260
11261     /**
11262      * Returns the sort state of the Store as an object with two properties:
11263      * <pre><code>
11264  field {String} The name of the field by which the Records are sorted
11265  direction {String} The sort order, "ASC" or "DESC"
11266      * </code></pre>
11267      */
11268     getSortState : function(){
11269         return this.sortInfo;
11270     },
11271
11272     // private
11273     applySort : function(){
11274         if(this.sortInfo && !this.remoteSort){
11275             var s = this.sortInfo, f = s.field;
11276             var st = this.fields.get(f).sortType;
11277             var fn = function(r1, r2){
11278                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11279                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11280             };
11281             this.data.sort(s.direction, fn);
11282             if(this.snapshot && this.snapshot != this.data){
11283                 this.snapshot.sort(s.direction, fn);
11284             }
11285         }
11286     },
11287
11288     /**
11289      * Sets the default sort column and order to be used by the next load operation.
11290      * @param {String} fieldName The name of the field to sort by.
11291      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11292      */
11293     setDefaultSort : function(field, dir){
11294         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11295     },
11296
11297     /**
11298      * Sort the Records.
11299      * If remote sorting is used, the sort is performed on the server, and the cache is
11300      * reloaded. If local sorting is used, the cache is sorted internally.
11301      * @param {String} fieldName The name of the field to sort by.
11302      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11303      */
11304     sort : function(fieldName, dir){
11305         var f = this.fields.get(fieldName);
11306         if(!dir){
11307             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11308             
11309             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11310                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11311             }else{
11312                 dir = f.sortDir;
11313             }
11314         }
11315         this.sortToggle[f.name] = dir;
11316         this.sortInfo = {field: f.name, direction: dir};
11317         if(!this.remoteSort){
11318             this.applySort();
11319             this.fireEvent("datachanged", this);
11320         }else{
11321             this.load(this.lastOptions);
11322         }
11323     },
11324
11325     /**
11326      * Calls the specified function for each of the Records in the cache.
11327      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11328      * Returning <em>false</em> aborts and exits the iteration.
11329      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11330      */
11331     each : function(fn, scope){
11332         this.data.each(fn, scope);
11333     },
11334
11335     /**
11336      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11337      * (e.g., during paging).
11338      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11339      */
11340     getModifiedRecords : function(){
11341         return this.modified;
11342     },
11343
11344     // private
11345     createFilterFn : function(property, value, anyMatch){
11346         if(!value.exec){ // not a regex
11347             value = String(value);
11348             if(value.length == 0){
11349                 return false;
11350             }
11351             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11352         }
11353         return function(r){
11354             return value.test(r.data[property]);
11355         };
11356     },
11357
11358     /**
11359      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11360      * @param {String} property A field on your records
11361      * @param {Number} start The record index to start at (defaults to 0)
11362      * @param {Number} end The last record index to include (defaults to length - 1)
11363      * @return {Number} The sum
11364      */
11365     sum : function(property, start, end){
11366         var rs = this.data.items, v = 0;
11367         start = start || 0;
11368         end = (end || end === 0) ? end : rs.length-1;
11369
11370         for(var i = start; i <= end; i++){
11371             v += (rs[i].data[property] || 0);
11372         }
11373         return v;
11374     },
11375
11376     /**
11377      * Filter the records by a specified property.
11378      * @param {String} field A field on your records
11379      * @param {String/RegExp} value Either a string that the field
11380      * should start with or a RegExp to test against the field
11381      * @param {Boolean} anyMatch True to match any part not just the beginning
11382      */
11383     filter : function(property, value, anyMatch){
11384         var fn = this.createFilterFn(property, value, anyMatch);
11385         return fn ? this.filterBy(fn) : this.clearFilter();
11386     },
11387
11388     /**
11389      * Filter by a function. The specified function will be called with each
11390      * record in this data source. If the function returns true the record is included,
11391      * otherwise it is filtered.
11392      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11393      * @param {Object} scope (optional) The scope of the function (defaults to this)
11394      */
11395     filterBy : function(fn, scope){
11396         this.snapshot = this.snapshot || this.data;
11397         this.data = this.queryBy(fn, scope||this);
11398         this.fireEvent("datachanged", this);
11399     },
11400
11401     /**
11402      * Query the records by a specified property.
11403      * @param {String} field A field on your records
11404      * @param {String/RegExp} value Either a string that the field
11405      * should start with or a RegExp to test against the field
11406      * @param {Boolean} anyMatch True to match any part not just the beginning
11407      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11408      */
11409     query : function(property, value, anyMatch){
11410         var fn = this.createFilterFn(property, value, anyMatch);
11411         return fn ? this.queryBy(fn) : this.data.clone();
11412     },
11413
11414     /**
11415      * Query by a function. The specified function will be called with each
11416      * record in this data source. If the function returns true the record is included
11417      * in the results.
11418      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11419      * @param {Object} scope (optional) The scope of the function (defaults to this)
11420       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11421      **/
11422     queryBy : function(fn, scope){
11423         var data = this.snapshot || this.data;
11424         return data.filterBy(fn, scope||this);
11425     },
11426
11427     /**
11428      * Collects unique values for a particular dataIndex from this store.
11429      * @param {String} dataIndex The property to collect
11430      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11431      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11432      * @return {Array} An array of the unique values
11433      **/
11434     collect : function(dataIndex, allowNull, bypassFilter){
11435         var d = (bypassFilter === true && this.snapshot) ?
11436                 this.snapshot.items : this.data.items;
11437         var v, sv, r = [], l = {};
11438         for(var i = 0, len = d.length; i < len; i++){
11439             v = d[i].data[dataIndex];
11440             sv = String(v);
11441             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11442                 l[sv] = true;
11443                 r[r.length] = v;
11444             }
11445         }
11446         return r;
11447     },
11448
11449     /**
11450      * Revert to a view of the Record cache with no filtering applied.
11451      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11452      */
11453     clearFilter : function(suppressEvent){
11454         if(this.snapshot && this.snapshot != this.data){
11455             this.data = this.snapshot;
11456             delete this.snapshot;
11457             if(suppressEvent !== true){
11458                 this.fireEvent("datachanged", this);
11459             }
11460         }
11461     },
11462
11463     // private
11464     afterEdit : function(record){
11465         if(this.modified.indexOf(record) == -1){
11466             this.modified.push(record);
11467         }
11468         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11469     },
11470     
11471     // private
11472     afterReject : function(record){
11473         this.modified.remove(record);
11474         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11475     },
11476
11477     // private
11478     afterCommit : function(record){
11479         this.modified.remove(record);
11480         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11481     },
11482
11483     /**
11484      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11485      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11486      */
11487     commitChanges : function(){
11488         var m = this.modified.slice(0);
11489         this.modified = [];
11490         for(var i = 0, len = m.length; i < len; i++){
11491             m[i].commit();
11492         }
11493     },
11494
11495     /**
11496      * Cancel outstanding changes on all changed records.
11497      */
11498     rejectChanges : function(){
11499         var m = this.modified.slice(0);
11500         this.modified = [];
11501         for(var i = 0, len = m.length; i < len; i++){
11502             m[i].reject();
11503         }
11504     },
11505
11506     onMetaChange : function(meta, rtype, o){
11507         this.recordType = rtype;
11508         this.fields = rtype.prototype.fields;
11509         delete this.snapshot;
11510         this.sortInfo = meta.sortInfo || this.sortInfo;
11511         this.modified = [];
11512         this.fireEvent('metachange', this, this.reader.meta);
11513     },
11514     
11515     moveIndex : function(data, type)
11516     {
11517         var index = this.indexOf(data);
11518         
11519         var newIndex = index + type;
11520         
11521         this.remove(data);
11522         
11523         this.insert(newIndex, data);
11524         
11525     }
11526 });/*
11527  * Based on:
11528  * Ext JS Library 1.1.1
11529  * Copyright(c) 2006-2007, Ext JS, LLC.
11530  *
11531  * Originally Released Under LGPL - original licence link has changed is not relivant.
11532  *
11533  * Fork - LGPL
11534  * <script type="text/javascript">
11535  */
11536
11537 /**
11538  * @class Roo.data.SimpleStore
11539  * @extends Roo.data.Store
11540  * Small helper class to make creating Stores from Array data easier.
11541  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11542  * @cfg {Array} fields An array of field definition objects, or field name strings.
11543  * @cfg {Array} data The multi-dimensional array of data
11544  * @constructor
11545  * @param {Object} config
11546  */
11547 Roo.data.SimpleStore = function(config){
11548     Roo.data.SimpleStore.superclass.constructor.call(this, {
11549         isLocal : true,
11550         reader: new Roo.data.ArrayReader({
11551                 id: config.id
11552             },
11553             Roo.data.Record.create(config.fields)
11554         ),
11555         proxy : new Roo.data.MemoryProxy(config.data)
11556     });
11557     this.load();
11558 };
11559 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11560  * Based on:
11561  * Ext JS Library 1.1.1
11562  * Copyright(c) 2006-2007, Ext JS, LLC.
11563  *
11564  * Originally Released Under LGPL - original licence link has changed is not relivant.
11565  *
11566  * Fork - LGPL
11567  * <script type="text/javascript">
11568  */
11569
11570 /**
11571 /**
11572  * @extends Roo.data.Store
11573  * @class Roo.data.JsonStore
11574  * Small helper class to make creating Stores for JSON data easier. <br/>
11575 <pre><code>
11576 var store = new Roo.data.JsonStore({
11577     url: 'get-images.php',
11578     root: 'images',
11579     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11580 });
11581 </code></pre>
11582  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11583  * JsonReader and HttpProxy (unless inline data is provided).</b>
11584  * @cfg {Array} fields An array of field definition objects, or field name strings.
11585  * @constructor
11586  * @param {Object} config
11587  */
11588 Roo.data.JsonStore = function(c){
11589     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11590         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11591         reader: new Roo.data.JsonReader(c, c.fields)
11592     }));
11593 };
11594 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11595  * Based on:
11596  * Ext JS Library 1.1.1
11597  * Copyright(c) 2006-2007, Ext JS, LLC.
11598  *
11599  * Originally Released Under LGPL - original licence link has changed is not relivant.
11600  *
11601  * Fork - LGPL
11602  * <script type="text/javascript">
11603  */
11604
11605  
11606 Roo.data.Field = function(config){
11607     if(typeof config == "string"){
11608         config = {name: config};
11609     }
11610     Roo.apply(this, config);
11611     
11612     if(!this.type){
11613         this.type = "auto";
11614     }
11615     
11616     var st = Roo.data.SortTypes;
11617     // named sortTypes are supported, here we look them up
11618     if(typeof this.sortType == "string"){
11619         this.sortType = st[this.sortType];
11620     }
11621     
11622     // set default sortType for strings and dates
11623     if(!this.sortType){
11624         switch(this.type){
11625             case "string":
11626                 this.sortType = st.asUCString;
11627                 break;
11628             case "date":
11629                 this.sortType = st.asDate;
11630                 break;
11631             default:
11632                 this.sortType = st.none;
11633         }
11634     }
11635
11636     // define once
11637     var stripRe = /[\$,%]/g;
11638
11639     // prebuilt conversion function for this field, instead of
11640     // switching every time we're reading a value
11641     if(!this.convert){
11642         var cv, dateFormat = this.dateFormat;
11643         switch(this.type){
11644             case "":
11645             case "auto":
11646             case undefined:
11647                 cv = function(v){ return v; };
11648                 break;
11649             case "string":
11650                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11651                 break;
11652             case "int":
11653                 cv = function(v){
11654                     return v !== undefined && v !== null && v !== '' ?
11655                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11656                     };
11657                 break;
11658             case "float":
11659                 cv = function(v){
11660                     return v !== undefined && v !== null && v !== '' ?
11661                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11662                     };
11663                 break;
11664             case "bool":
11665             case "boolean":
11666                 cv = function(v){ return v === true || v === "true" || v == 1; };
11667                 break;
11668             case "date":
11669                 cv = function(v){
11670                     if(!v){
11671                         return '';
11672                     }
11673                     if(v instanceof Date){
11674                         return v;
11675                     }
11676                     if(dateFormat){
11677                         if(dateFormat == "timestamp"){
11678                             return new Date(v*1000);
11679                         }
11680                         return Date.parseDate(v, dateFormat);
11681                     }
11682                     var parsed = Date.parse(v);
11683                     return parsed ? new Date(parsed) : null;
11684                 };
11685              break;
11686             
11687         }
11688         this.convert = cv;
11689     }
11690 };
11691
11692 Roo.data.Field.prototype = {
11693     dateFormat: null,
11694     defaultValue: "",
11695     mapping: null,
11696     sortType : null,
11697     sortDir : "ASC"
11698 };/*
11699  * Based on:
11700  * Ext JS Library 1.1.1
11701  * Copyright(c) 2006-2007, Ext JS, LLC.
11702  *
11703  * Originally Released Under LGPL - original licence link has changed is not relivant.
11704  *
11705  * Fork - LGPL
11706  * <script type="text/javascript">
11707  */
11708  
11709 // Base class for reading structured data from a data source.  This class is intended to be
11710 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11711
11712 /**
11713  * @class Roo.data.DataReader
11714  * Base class for reading structured data from a data source.  This class is intended to be
11715  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11716  */
11717
11718 Roo.data.DataReader = function(meta, recordType){
11719     
11720     this.meta = meta;
11721     
11722     this.recordType = recordType instanceof Array ? 
11723         Roo.data.Record.create(recordType) : recordType;
11724 };
11725
11726 Roo.data.DataReader.prototype = {
11727      /**
11728      * Create an empty record
11729      * @param {Object} data (optional) - overlay some values
11730      * @return {Roo.data.Record} record created.
11731      */
11732     newRow :  function(d) {
11733         var da =  {};
11734         this.recordType.prototype.fields.each(function(c) {
11735             switch( c.type) {
11736                 case 'int' : da[c.name] = 0; break;
11737                 case 'date' : da[c.name] = new Date(); break;
11738                 case 'float' : da[c.name] = 0.0; break;
11739                 case 'boolean' : da[c.name] = false; break;
11740                 default : da[c.name] = ""; break;
11741             }
11742             
11743         });
11744         return new this.recordType(Roo.apply(da, d));
11745     }
11746     
11747 };/*
11748  * Based on:
11749  * Ext JS Library 1.1.1
11750  * Copyright(c) 2006-2007, Ext JS, LLC.
11751  *
11752  * Originally Released Under LGPL - original licence link has changed is not relivant.
11753  *
11754  * Fork - LGPL
11755  * <script type="text/javascript">
11756  */
11757
11758 /**
11759  * @class Roo.data.DataProxy
11760  * @extends Roo.data.Observable
11761  * This class is an abstract base class for implementations which provide retrieval of
11762  * unformatted data objects.<br>
11763  * <p>
11764  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11765  * (of the appropriate type which knows how to parse the data object) to provide a block of
11766  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11767  * <p>
11768  * Custom implementations must implement the load method as described in
11769  * {@link Roo.data.HttpProxy#load}.
11770  */
11771 Roo.data.DataProxy = function(){
11772     this.addEvents({
11773         /**
11774          * @event beforeload
11775          * Fires before a network request is made to retrieve a data object.
11776          * @param {Object} This DataProxy object.
11777          * @param {Object} params The params parameter to the load function.
11778          */
11779         beforeload : true,
11780         /**
11781          * @event load
11782          * Fires before the load method's callback is called.
11783          * @param {Object} This DataProxy object.
11784          * @param {Object} o The data object.
11785          * @param {Object} arg The callback argument object passed to the load function.
11786          */
11787         load : true,
11788         /**
11789          * @event loadexception
11790          * Fires if an Exception occurs during data retrieval.
11791          * @param {Object} This DataProxy object.
11792          * @param {Object} o The data object.
11793          * @param {Object} arg The callback argument object passed to the load function.
11794          * @param {Object} e The Exception.
11795          */
11796         loadexception : true
11797     });
11798     Roo.data.DataProxy.superclass.constructor.call(this);
11799 };
11800
11801 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11802
11803     /**
11804      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
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.MemoryProxy
11818  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11819  * to the Reader when its load method is called.
11820  * @constructor
11821  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11822  */
11823 Roo.data.MemoryProxy = function(data){
11824     if (data.data) {
11825         data = data.data;
11826     }
11827     Roo.data.MemoryProxy.superclass.constructor.call(this);
11828     this.data = data;
11829 };
11830
11831 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11832     
11833     /**
11834      * Load data from the requested source (in this case an in-memory
11835      * data object passed to the constructor), read the data object into
11836      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11837      * process that block using the passed callback.
11838      * @param {Object} params This parameter is not used by the MemoryProxy class.
11839      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11840      * object into a block of Roo.data.Records.
11841      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11842      * The function must be passed <ul>
11843      * <li>The Record block object</li>
11844      * <li>The "arg" argument from the load function</li>
11845      * <li>A boolean success indicator</li>
11846      * </ul>
11847      * @param {Object} scope The scope in which to call the callback
11848      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11849      */
11850     load : function(params, reader, callback, scope, arg){
11851         params = params || {};
11852         var result;
11853         try {
11854             result = reader.readRecords(this.data);
11855         }catch(e){
11856             this.fireEvent("loadexception", this, arg, null, e);
11857             callback.call(scope, null, arg, false);
11858             return;
11859         }
11860         callback.call(scope, result, arg, true);
11861     },
11862     
11863     // private
11864     update : function(params, records){
11865         
11866     }
11867 });/*
11868  * Based on:
11869  * Ext JS Library 1.1.1
11870  * Copyright(c) 2006-2007, Ext JS, LLC.
11871  *
11872  * Originally Released Under LGPL - original licence link has changed is not relivant.
11873  *
11874  * Fork - LGPL
11875  * <script type="text/javascript">
11876  */
11877 /**
11878  * @class Roo.data.HttpProxy
11879  * @extends Roo.data.DataProxy
11880  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11881  * configured to reference a certain URL.<br><br>
11882  * <p>
11883  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11884  * from which the running page was served.<br><br>
11885  * <p>
11886  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11887  * <p>
11888  * Be aware that to enable the browser to parse an XML document, the server must set
11889  * the Content-Type header in the HTTP response to "text/xml".
11890  * @constructor
11891  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11892  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11893  * will be used to make the request.
11894  */
11895 Roo.data.HttpProxy = function(conn){
11896     Roo.data.HttpProxy.superclass.constructor.call(this);
11897     // is conn a conn config or a real conn?
11898     this.conn = conn;
11899     this.useAjax = !conn || !conn.events;
11900   
11901 };
11902
11903 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11904     // thse are take from connection...
11905     
11906     /**
11907      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11908      */
11909     /**
11910      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11911      * extra parameters to each request made by this object. (defaults to undefined)
11912      */
11913     /**
11914      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11915      *  to each request made by this object. (defaults to undefined)
11916      */
11917     /**
11918      * @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)
11919      */
11920     /**
11921      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11922      */
11923      /**
11924      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11925      * @type Boolean
11926      */
11927   
11928
11929     /**
11930      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11931      * @type Boolean
11932      */
11933     /**
11934      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11935      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11936      * a finer-grained basis than the DataProxy events.
11937      */
11938     getConnection : function(){
11939         return this.useAjax ? Roo.Ajax : this.conn;
11940     },
11941
11942     /**
11943      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11944      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11945      * process that block using the passed callback.
11946      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11947      * for the request to the remote server.
11948      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11949      * object into a block of Roo.data.Records.
11950      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11951      * The function must be passed <ul>
11952      * <li>The Record block object</li>
11953      * <li>The "arg" argument from the load function</li>
11954      * <li>A boolean success indicator</li>
11955      * </ul>
11956      * @param {Object} scope The scope in which to call the callback
11957      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11958      */
11959     load : function(params, reader, callback, scope, arg){
11960         if(this.fireEvent("beforeload", this, params) !== false){
11961             var  o = {
11962                 params : params || {},
11963                 request: {
11964                     callback : callback,
11965                     scope : scope,
11966                     arg : arg
11967                 },
11968                 reader: reader,
11969                 callback : this.loadResponse,
11970                 scope: this
11971             };
11972             if(this.useAjax){
11973                 Roo.applyIf(o, this.conn);
11974                 if(this.activeRequest){
11975                     Roo.Ajax.abort(this.activeRequest);
11976                 }
11977                 this.activeRequest = Roo.Ajax.request(o);
11978             }else{
11979                 this.conn.request(o);
11980             }
11981         }else{
11982             callback.call(scope||this, null, arg, false);
11983         }
11984     },
11985
11986     // private
11987     loadResponse : function(o, success, response){
11988         delete this.activeRequest;
11989         if(!success){
11990             this.fireEvent("loadexception", this, o, response);
11991             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11992             return;
11993         }
11994         var result;
11995         try {
11996             result = o.reader.read(response);
11997         }catch(e){
11998             this.fireEvent("loadexception", this, o, response, e);
11999             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12000             return;
12001         }
12002         
12003         this.fireEvent("load", this, o, o.request.arg);
12004         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12005     },
12006
12007     // private
12008     update : function(dataSet){
12009
12010     },
12011
12012     // private
12013     updateResponse : function(dataSet){
12014
12015     }
12016 });/*
12017  * Based on:
12018  * Ext JS Library 1.1.1
12019  * Copyright(c) 2006-2007, Ext JS, LLC.
12020  *
12021  * Originally Released Under LGPL - original licence link has changed is not relivant.
12022  *
12023  * Fork - LGPL
12024  * <script type="text/javascript">
12025  */
12026
12027 /**
12028  * @class Roo.data.ScriptTagProxy
12029  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12030  * other than the originating domain of the running page.<br><br>
12031  * <p>
12032  * <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
12033  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12034  * <p>
12035  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12036  * source code that is used as the source inside a &lt;script> tag.<br><br>
12037  * <p>
12038  * In order for the browser to process the returned data, the server must wrap the data object
12039  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12040  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12041  * depending on whether the callback name was passed:
12042  * <p>
12043  * <pre><code>
12044 boolean scriptTag = false;
12045 String cb = request.getParameter("callback");
12046 if (cb != null) {
12047     scriptTag = true;
12048     response.setContentType("text/javascript");
12049 } else {
12050     response.setContentType("application/x-json");
12051 }
12052 Writer out = response.getWriter();
12053 if (scriptTag) {
12054     out.write(cb + "(");
12055 }
12056 out.print(dataBlock.toJsonString());
12057 if (scriptTag) {
12058     out.write(");");
12059 }
12060 </pre></code>
12061  *
12062  * @constructor
12063  * @param {Object} config A configuration object.
12064  */
12065 Roo.data.ScriptTagProxy = function(config){
12066     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12067     Roo.apply(this, config);
12068     this.head = document.getElementsByTagName("head")[0];
12069 };
12070
12071 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12072
12073 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12074     /**
12075      * @cfg {String} url The URL from which to request the data object.
12076      */
12077     /**
12078      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12079      */
12080     timeout : 30000,
12081     /**
12082      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12083      * the server the name of the callback function set up by the load call to process the returned data object.
12084      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12085      * javascript output which calls this named function passing the data object as its only parameter.
12086      */
12087     callbackParam : "callback",
12088     /**
12089      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12090      * name to the request.
12091      */
12092     nocache : true,
12093
12094     /**
12095      * Load data from the configured URL, read the data object into
12096      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12097      * process that block using the passed callback.
12098      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12099      * for the request to the remote server.
12100      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12101      * object into a block of Roo.data.Records.
12102      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12103      * The function must be passed <ul>
12104      * <li>The Record block object</li>
12105      * <li>The "arg" argument from the load function</li>
12106      * <li>A boolean success indicator</li>
12107      * </ul>
12108      * @param {Object} scope The scope in which to call the callback
12109      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12110      */
12111     load : function(params, reader, callback, scope, arg){
12112         if(this.fireEvent("beforeload", this, params) !== false){
12113
12114             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12115
12116             var url = this.url;
12117             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12118             if(this.nocache){
12119                 url += "&_dc=" + (new Date().getTime());
12120             }
12121             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12122             var trans = {
12123                 id : transId,
12124                 cb : "stcCallback"+transId,
12125                 scriptId : "stcScript"+transId,
12126                 params : params,
12127                 arg : arg,
12128                 url : url,
12129                 callback : callback,
12130                 scope : scope,
12131                 reader : reader
12132             };
12133             var conn = this;
12134
12135             window[trans.cb] = function(o){
12136                 conn.handleResponse(o, trans);
12137             };
12138
12139             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12140
12141             if(this.autoAbort !== false){
12142                 this.abort();
12143             }
12144
12145             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12146
12147             var script = document.createElement("script");
12148             script.setAttribute("src", url);
12149             script.setAttribute("type", "text/javascript");
12150             script.setAttribute("id", trans.scriptId);
12151             this.head.appendChild(script);
12152
12153             this.trans = trans;
12154         }else{
12155             callback.call(scope||this, null, arg, false);
12156         }
12157     },
12158
12159     // private
12160     isLoading : function(){
12161         return this.trans ? true : false;
12162     },
12163
12164     /**
12165      * Abort the current server request.
12166      */
12167     abort : function(){
12168         if(this.isLoading()){
12169             this.destroyTrans(this.trans);
12170         }
12171     },
12172
12173     // private
12174     destroyTrans : function(trans, isLoaded){
12175         this.head.removeChild(document.getElementById(trans.scriptId));
12176         clearTimeout(trans.timeoutId);
12177         if(isLoaded){
12178             window[trans.cb] = undefined;
12179             try{
12180                 delete window[trans.cb];
12181             }catch(e){}
12182         }else{
12183             // if hasn't been loaded, wait for load to remove it to prevent script error
12184             window[trans.cb] = function(){
12185                 window[trans.cb] = undefined;
12186                 try{
12187                     delete window[trans.cb];
12188                 }catch(e){}
12189             };
12190         }
12191     },
12192
12193     // private
12194     handleResponse : function(o, trans){
12195         this.trans = false;
12196         this.destroyTrans(trans, true);
12197         var result;
12198         try {
12199             result = trans.reader.readRecords(o);
12200         }catch(e){
12201             this.fireEvent("loadexception", this, o, trans.arg, e);
12202             trans.callback.call(trans.scope||window, null, trans.arg, false);
12203             return;
12204         }
12205         this.fireEvent("load", this, o, trans.arg);
12206         trans.callback.call(trans.scope||window, result, trans.arg, true);
12207     },
12208
12209     // private
12210     handleFailure : function(trans){
12211         this.trans = false;
12212         this.destroyTrans(trans, false);
12213         this.fireEvent("loadexception", this, null, trans.arg);
12214         trans.callback.call(trans.scope||window, null, trans.arg, false);
12215     }
12216 });/*
12217  * Based on:
12218  * Ext JS Library 1.1.1
12219  * Copyright(c) 2006-2007, Ext JS, LLC.
12220  *
12221  * Originally Released Under LGPL - original licence link has changed is not relivant.
12222  *
12223  * Fork - LGPL
12224  * <script type="text/javascript">
12225  */
12226
12227 /**
12228  * @class Roo.data.JsonReader
12229  * @extends Roo.data.DataReader
12230  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12231  * based on mappings in a provided Roo.data.Record constructor.
12232  * 
12233  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12234  * in the reply previously. 
12235  * 
12236  * <p>
12237  * Example code:
12238  * <pre><code>
12239 var RecordDef = Roo.data.Record.create([
12240     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12241     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12242 ]);
12243 var myReader = new Roo.data.JsonReader({
12244     totalProperty: "results",    // The property which contains the total dataset size (optional)
12245     root: "rows",                // The property which contains an Array of row objects
12246     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12247 }, RecordDef);
12248 </code></pre>
12249  * <p>
12250  * This would consume a JSON file like this:
12251  * <pre><code>
12252 { 'results': 2, 'rows': [
12253     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12254     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12255 }
12256 </code></pre>
12257  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12258  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12259  * paged from the remote server.
12260  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12261  * @cfg {String} root name of the property which contains the Array of row objects.
12262  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12263  * @cfg {Array} fields Array of field definition objects
12264  * @constructor
12265  * Create a new JsonReader
12266  * @param {Object} meta Metadata configuration options
12267  * @param {Object} recordType Either an Array of field definition objects,
12268  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12269  */
12270 Roo.data.JsonReader = function(meta, recordType){
12271     
12272     meta = meta || {};
12273     // set some defaults:
12274     Roo.applyIf(meta, {
12275         totalProperty: 'total',
12276         successProperty : 'success',
12277         root : 'data',
12278         id : 'id'
12279     });
12280     
12281     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12282 };
12283 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12284     
12285     /**
12286      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12287      * Used by Store query builder to append _requestMeta to params.
12288      * 
12289      */
12290     metaFromRemote : false,
12291     /**
12292      * This method is only used by a DataProxy which has retrieved data from a remote server.
12293      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12294      * @return {Object} data A data block which is used by an Roo.data.Store object as
12295      * a cache of Roo.data.Records.
12296      */
12297     read : function(response){
12298         var json = response.responseText;
12299        
12300         var o = /* eval:var:o */ eval("("+json+")");
12301         if(!o) {
12302             throw {message: "JsonReader.read: Json object not found"};
12303         }
12304         
12305         if(o.metaData){
12306             
12307             delete this.ef;
12308             this.metaFromRemote = true;
12309             this.meta = o.metaData;
12310             this.recordType = Roo.data.Record.create(o.metaData.fields);
12311             this.onMetaChange(this.meta, this.recordType, o);
12312         }
12313         return this.readRecords(o);
12314     },
12315
12316     // private function a store will implement
12317     onMetaChange : function(meta, recordType, o){
12318
12319     },
12320
12321     /**
12322          * @ignore
12323          */
12324     simpleAccess: function(obj, subsc) {
12325         return obj[subsc];
12326     },
12327
12328         /**
12329          * @ignore
12330          */
12331     getJsonAccessor: function(){
12332         var re = /[\[\.]/;
12333         return function(expr) {
12334             try {
12335                 return(re.test(expr))
12336                     ? new Function("obj", "return obj." + expr)
12337                     : function(obj){
12338                         return obj[expr];
12339                     };
12340             } catch(e){}
12341             return Roo.emptyFn;
12342         };
12343     }(),
12344
12345     /**
12346      * Create a data block containing Roo.data.Records from an XML document.
12347      * @param {Object} o An object which contains an Array of row objects in the property specified
12348      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12349      * which contains the total size of the dataset.
12350      * @return {Object} data A data block which is used by an Roo.data.Store object as
12351      * a cache of Roo.data.Records.
12352      */
12353     readRecords : function(o){
12354         /**
12355          * After any data loads, the raw JSON data is available for further custom processing.
12356          * @type Object
12357          */
12358         this.o = o;
12359         var s = this.meta, Record = this.recordType,
12360             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12361
12362 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12363         if (!this.ef) {
12364             if(s.totalProperty) {
12365                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12366                 }
12367                 if(s.successProperty) {
12368                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12369                 }
12370                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12371                 if (s.id) {
12372                         var g = this.getJsonAccessor(s.id);
12373                         this.getId = function(rec) {
12374                                 var r = g(rec);  
12375                                 return (r === undefined || r === "") ? null : r;
12376                         };
12377                 } else {
12378                         this.getId = function(){return null;};
12379                 }
12380             this.ef = [];
12381             for(var jj = 0; jj < fl; jj++){
12382                 f = fi[jj];
12383                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12384                 this.ef[jj] = this.getJsonAccessor(map);
12385             }
12386         }
12387
12388         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12389         if(s.totalProperty){
12390             var vt = parseInt(this.getTotal(o), 10);
12391             if(!isNaN(vt)){
12392                 totalRecords = vt;
12393             }
12394         }
12395         if(s.successProperty){
12396             var vs = this.getSuccess(o);
12397             if(vs === false || vs === 'false'){
12398                 success = false;
12399             }
12400         }
12401         var records = [];
12402         for(var i = 0; i < c; i++){
12403                 var n = root[i];
12404             var values = {};
12405             var id = this.getId(n);
12406             for(var j = 0; j < fl; j++){
12407                 f = fi[j];
12408             var v = this.ef[j](n);
12409             if (!f.convert) {
12410                 Roo.log('missing convert for ' + f.name);
12411                 Roo.log(f);
12412                 continue;
12413             }
12414             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12415             }
12416             var record = new Record(values, id);
12417             record.json = n;
12418             records[i] = record;
12419         }
12420         return {
12421             raw : o,
12422             success : success,
12423             records : records,
12424             totalRecords : totalRecords
12425         };
12426     }
12427 });/*
12428  * Based on:
12429  * Ext JS Library 1.1.1
12430  * Copyright(c) 2006-2007, Ext JS, LLC.
12431  *
12432  * Originally Released Under LGPL - original licence link has changed is not relivant.
12433  *
12434  * Fork - LGPL
12435  * <script type="text/javascript">
12436  */
12437
12438 /**
12439  * @class Roo.data.ArrayReader
12440  * @extends Roo.data.DataReader
12441  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12442  * Each element of that Array represents a row of data fields. The
12443  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12444  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12445  * <p>
12446  * Example code:.
12447  * <pre><code>
12448 var RecordDef = Roo.data.Record.create([
12449     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12450     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12451 ]);
12452 var myReader = new Roo.data.ArrayReader({
12453     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12454 }, RecordDef);
12455 </code></pre>
12456  * <p>
12457  * This would consume an Array like this:
12458  * <pre><code>
12459 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12460   </code></pre>
12461  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12462  * @constructor
12463  * Create a new JsonReader
12464  * @param {Object} meta Metadata configuration options.
12465  * @param {Object} recordType Either an Array of field definition objects
12466  * as specified to {@link Roo.data.Record#create},
12467  * or an {@link Roo.data.Record} object
12468  * created using {@link Roo.data.Record#create}.
12469  */
12470 Roo.data.ArrayReader = function(meta, recordType){
12471     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12472 };
12473
12474 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12475     /**
12476      * Create a data block containing Roo.data.Records from an XML document.
12477      * @param {Object} o An Array of row objects which represents the dataset.
12478      * @return {Object} data A data block which is used by an Roo.data.Store object as
12479      * a cache of Roo.data.Records.
12480      */
12481     readRecords : function(o){
12482         var sid = this.meta ? this.meta.id : null;
12483         var recordType = this.recordType, fields = recordType.prototype.fields;
12484         var records = [];
12485         var root = o;
12486             for(var i = 0; i < root.length; i++){
12487                     var n = root[i];
12488                 var values = {};
12489                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12490                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12491                 var f = fields.items[j];
12492                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12493                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12494                 v = f.convert(v);
12495                 values[f.name] = v;
12496             }
12497                 var record = new recordType(values, id);
12498                 record.json = n;
12499                 records[records.length] = record;
12500             }
12501             return {
12502                 records : records,
12503                 totalRecords : records.length
12504             };
12505     }
12506 });/*
12507  * - LGPL
12508  * * 
12509  */
12510
12511 /**
12512  * @class Roo.bootstrap.ComboBox
12513  * @extends Roo.bootstrap.TriggerField
12514  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12515  * @cfg {Boolean} append (true|false) default false
12516  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12517  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12518  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12519  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12520  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12521  * @cfg {Boolean} animate default true
12522  * @cfg {Boolean} emptyResultText only for touch device
12523  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12524  * @cfg {String} emptyTitle default ''
12525  * @constructor
12526  * Create a new ComboBox.
12527  * @param {Object} config Configuration options
12528  */
12529 Roo.bootstrap.ComboBox = function(config){
12530     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12531     this.addEvents({
12532         /**
12533          * @event expand
12534          * Fires when the dropdown list is expanded
12535         * @param {Roo.bootstrap.ComboBox} combo This combo box
12536         */
12537         'expand' : true,
12538         /**
12539          * @event collapse
12540          * Fires when the dropdown list is collapsed
12541         * @param {Roo.bootstrap.ComboBox} combo This combo box
12542         */
12543         'collapse' : true,
12544         /**
12545          * @event beforeselect
12546          * Fires before a list item is selected. Return false to cancel the selection.
12547         * @param {Roo.bootstrap.ComboBox} combo This combo box
12548         * @param {Roo.data.Record} record The data record returned from the underlying store
12549         * @param {Number} index The index of the selected item in the dropdown list
12550         */
12551         'beforeselect' : true,
12552         /**
12553          * @event select
12554          * Fires when a list item is selected
12555         * @param {Roo.bootstrap.ComboBox} combo This combo box
12556         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12557         * @param {Number} index The index of the selected item in the dropdown list
12558         */
12559         'select' : true,
12560         /**
12561          * @event beforequery
12562          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12563          * The event object passed has these properties:
12564         * @param {Roo.bootstrap.ComboBox} combo This combo box
12565         * @param {String} query The query
12566         * @param {Boolean} forceAll true to force "all" query
12567         * @param {Boolean} cancel true to cancel the query
12568         * @param {Object} e The query event object
12569         */
12570         'beforequery': true,
12571          /**
12572          * @event add
12573          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12574         * @param {Roo.bootstrap.ComboBox} combo This combo box
12575         */
12576         'add' : true,
12577         /**
12578          * @event edit
12579          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12580         * @param {Roo.bootstrap.ComboBox} combo This combo box
12581         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12582         */
12583         'edit' : true,
12584         /**
12585          * @event remove
12586          * Fires when the remove value from the combobox array
12587         * @param {Roo.bootstrap.ComboBox} combo This combo box
12588         */
12589         'remove' : true,
12590         /**
12591          * @event afterremove
12592          * Fires when the remove value from the combobox array
12593         * @param {Roo.bootstrap.ComboBox} combo This combo box
12594         */
12595         'afterremove' : true,
12596         /**
12597          * @event specialfilter
12598          * Fires when specialfilter
12599             * @param {Roo.bootstrap.ComboBox} combo This combo box
12600             */
12601         'specialfilter' : true,
12602         /**
12603          * @event tick
12604          * Fires when tick the element
12605             * @param {Roo.bootstrap.ComboBox} combo This combo box
12606             */
12607         'tick' : true,
12608         /**
12609          * @event touchviewdisplay
12610          * Fires when touch view require special display (default is using displayField)
12611             * @param {Roo.bootstrap.ComboBox} combo This combo box
12612             * @param {Object} cfg set html .
12613             */
12614         'touchviewdisplay' : true
12615         
12616     });
12617     
12618     this.item = [];
12619     this.tickItems = [];
12620     
12621     this.selectedIndex = -1;
12622     if(this.mode == 'local'){
12623         if(config.queryDelay === undefined){
12624             this.queryDelay = 10;
12625         }
12626         if(config.minChars === undefined){
12627             this.minChars = 0;
12628         }
12629     }
12630 };
12631
12632 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12633      
12634     /**
12635      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12636      * rendering into an Roo.Editor, defaults to false)
12637      */
12638     /**
12639      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12640      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12641      */
12642     /**
12643      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12644      */
12645     /**
12646      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12647      * the dropdown list (defaults to undefined, with no header element)
12648      */
12649
12650      /**
12651      * @cfg {String/Roo.Template} tpl The template to use to render the output
12652      */
12653      
12654      /**
12655      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12656      */
12657     listWidth: undefined,
12658     /**
12659      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12660      * mode = 'remote' or 'text' if mode = 'local')
12661      */
12662     displayField: undefined,
12663     
12664     /**
12665      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12666      * mode = 'remote' or 'value' if mode = 'local'). 
12667      * Note: use of a valueField requires the user make a selection
12668      * in order for a value to be mapped.
12669      */
12670     valueField: undefined,
12671     /**
12672      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12673      */
12674     modalTitle : '',
12675     
12676     /**
12677      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12678      * field's data value (defaults to the underlying DOM element's name)
12679      */
12680     hiddenName: undefined,
12681     /**
12682      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12683      */
12684     listClass: '',
12685     /**
12686      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12687      */
12688     selectedClass: 'active',
12689     
12690     /**
12691      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12692      */
12693     shadow:'sides',
12694     /**
12695      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12696      * anchor positions (defaults to 'tl-bl')
12697      */
12698     listAlign: 'tl-bl?',
12699     /**
12700      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12701      */
12702     maxHeight: 300,
12703     /**
12704      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12705      * query specified by the allQuery config option (defaults to 'query')
12706      */
12707     triggerAction: 'query',
12708     /**
12709      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12710      * (defaults to 4, does not apply if editable = false)
12711      */
12712     minChars : 4,
12713     /**
12714      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12715      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12716      */
12717     typeAhead: false,
12718     /**
12719      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12720      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12721      */
12722     queryDelay: 500,
12723     /**
12724      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12725      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12726      */
12727     pageSize: 0,
12728     /**
12729      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12730      * when editable = true (defaults to false)
12731      */
12732     selectOnFocus:false,
12733     /**
12734      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12735      */
12736     queryParam: 'query',
12737     /**
12738      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12739      * when mode = 'remote' (defaults to 'Loading...')
12740      */
12741     loadingText: 'Loading...',
12742     /**
12743      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12744      */
12745     resizable: false,
12746     /**
12747      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12748      */
12749     handleHeight : 8,
12750     /**
12751      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12752      * traditional select (defaults to true)
12753      */
12754     editable: true,
12755     /**
12756      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12757      */
12758     allQuery: '',
12759     /**
12760      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12761      */
12762     mode: 'remote',
12763     /**
12764      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12765      * listWidth has a higher value)
12766      */
12767     minListWidth : 70,
12768     /**
12769      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12770      * allow the user to set arbitrary text into the field (defaults to false)
12771      */
12772     forceSelection:false,
12773     /**
12774      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12775      * if typeAhead = true (defaults to 250)
12776      */
12777     typeAheadDelay : 250,
12778     /**
12779      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12780      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12781      */
12782     valueNotFoundText : undefined,
12783     /**
12784      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12785      */
12786     blockFocus : false,
12787     
12788     /**
12789      * @cfg {Boolean} disableClear Disable showing of clear button.
12790      */
12791     disableClear : false,
12792     /**
12793      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12794      */
12795     alwaysQuery : false,
12796     
12797     /**
12798      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12799      */
12800     multiple : false,
12801     
12802     /**
12803      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12804      */
12805     invalidClass : "has-warning",
12806     
12807     /**
12808      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12809      */
12810     validClass : "has-success",
12811     
12812     /**
12813      * @cfg {Boolean} specialFilter (true|false) special filter default false
12814      */
12815     specialFilter : false,
12816     
12817     /**
12818      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12819      */
12820     mobileTouchView : true,
12821     
12822     /**
12823      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12824      */
12825     useNativeIOS : false,
12826     
12827     ios_options : false,
12828     
12829     //private
12830     addicon : false,
12831     editicon: false,
12832     
12833     page: 0,
12834     hasQuery: false,
12835     append: false,
12836     loadNext: false,
12837     autoFocus : true,
12838     tickable : false,
12839     btnPosition : 'right',
12840     triggerList : true,
12841     showToggleBtn : true,
12842     animate : true,
12843     emptyResultText: 'Empty',
12844     triggerText : 'Select',
12845     emptyTitle : '',
12846     
12847     // element that contains real text value.. (when hidden is used..)
12848     
12849     getAutoCreate : function()
12850     {   
12851         var cfg = false;
12852         //render
12853         /*
12854          * Render classic select for iso
12855          */
12856         
12857         if(Roo.isIOS && this.useNativeIOS){
12858             cfg = this.getAutoCreateNativeIOS();
12859             return cfg;
12860         }
12861         
12862         /*
12863          * Touch Devices
12864          */
12865         
12866         if(Roo.isTouch && this.mobileTouchView){
12867             cfg = this.getAutoCreateTouchView();
12868             return cfg;;
12869         }
12870         
12871         /*
12872          *  Normal ComboBox
12873          */
12874         if(!this.tickable){
12875             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12876             return cfg;
12877         }
12878         
12879         /*
12880          *  ComboBox with tickable selections
12881          */
12882              
12883         var align = this.labelAlign || this.parentLabelAlign();
12884         
12885         cfg = {
12886             cls : 'form-group roo-combobox-tickable' //input-group
12887         };
12888         
12889         var btn_text_select = '';
12890         var btn_text_done = '';
12891         var btn_text_cancel = '';
12892         
12893         if (this.btn_text_show) {
12894             btn_text_select = 'Select';
12895             btn_text_done = 'Done';
12896             btn_text_cancel = 'Cancel'; 
12897         }
12898         
12899         var buttons = {
12900             tag : 'div',
12901             cls : 'tickable-buttons',
12902             cn : [
12903                 {
12904                     tag : 'button',
12905                     type : 'button',
12906                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12907                     //html : this.triggerText
12908                     html: btn_text_select
12909                 },
12910                 {
12911                     tag : 'button',
12912                     type : 'button',
12913                     name : 'ok',
12914                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12915                     //html : 'Done'
12916                     html: btn_text_done
12917                 },
12918                 {
12919                     tag : 'button',
12920                     type : 'button',
12921                     name : 'cancel',
12922                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12923                     //html : 'Cancel'
12924                     html: btn_text_cancel
12925                 }
12926             ]
12927         };
12928         
12929         if(this.editable){
12930             buttons.cn.unshift({
12931                 tag: 'input',
12932                 cls: 'roo-select2-search-field-input'
12933             });
12934         }
12935         
12936         var _this = this;
12937         
12938         Roo.each(buttons.cn, function(c){
12939             if (_this.size) {
12940                 c.cls += ' btn-' + _this.size;
12941             }
12942
12943             if (_this.disabled) {
12944                 c.disabled = true;
12945             }
12946         });
12947         
12948         var box = {
12949             tag: 'div',
12950             cn: [
12951                 {
12952                     tag: 'input',
12953                     type : 'hidden',
12954                     cls: 'form-hidden-field'
12955                 },
12956                 {
12957                     tag: 'ul',
12958                     cls: 'roo-select2-choices',
12959                     cn:[
12960                         {
12961                             tag: 'li',
12962                             cls: 'roo-select2-search-field',
12963                             cn: [
12964                                 buttons
12965                             ]
12966                         }
12967                     ]
12968                 }
12969             ]
12970         };
12971         
12972         var combobox = {
12973             cls: 'roo-select2-container input-group roo-select2-container-multi',
12974             cn: [
12975                 box
12976 //                {
12977 //                    tag: 'ul',
12978 //                    cls: 'typeahead typeahead-long dropdown-menu',
12979 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12980 //                }
12981             ]
12982         };
12983         
12984         if(this.hasFeedback && !this.allowBlank){
12985             
12986             var feedback = {
12987                 tag: 'span',
12988                 cls: 'glyphicon form-control-feedback'
12989             };
12990
12991             combobox.cn.push(feedback);
12992         }
12993         
12994         
12995         if (align ==='left' && this.fieldLabel.length) {
12996             
12997             cfg.cls += ' roo-form-group-label-left';
12998             
12999             cfg.cn = [
13000                 {
13001                     tag : 'i',
13002                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13003                     tooltip : 'This field is required'
13004                 },
13005                 {
13006                     tag: 'label',
13007                     'for' :  id,
13008                     cls : 'control-label',
13009                     html : this.fieldLabel
13010
13011                 },
13012                 {
13013                     cls : "", 
13014                     cn: [
13015                         combobox
13016                     ]
13017                 }
13018
13019             ];
13020             
13021             var labelCfg = cfg.cn[1];
13022             var contentCfg = cfg.cn[2];
13023             
13024
13025             if(this.indicatorpos == 'right'){
13026                 
13027                 cfg.cn = [
13028                     {
13029                         tag: 'label',
13030                         'for' :  id,
13031                         cls : 'control-label',
13032                         cn : [
13033                             {
13034                                 tag : 'span',
13035                                 html : this.fieldLabel
13036                             },
13037                             {
13038                                 tag : 'i',
13039                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13040                                 tooltip : 'This field is required'
13041                             }
13042                         ]
13043                     },
13044                     {
13045                         cls : "",
13046                         cn: [
13047                             combobox
13048                         ]
13049                     }
13050
13051                 ];
13052                 
13053                 
13054                 
13055                 labelCfg = cfg.cn[0];
13056                 contentCfg = cfg.cn[1];
13057             
13058             }
13059             
13060             if(this.labelWidth > 12){
13061                 labelCfg.style = "width: " + this.labelWidth + 'px';
13062             }
13063             
13064             if(this.labelWidth < 13 && this.labelmd == 0){
13065                 this.labelmd = this.labelWidth;
13066             }
13067             
13068             if(this.labellg > 0){
13069                 labelCfg.cls += ' col-lg-' + this.labellg;
13070                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13071             }
13072             
13073             if(this.labelmd > 0){
13074                 labelCfg.cls += ' col-md-' + this.labelmd;
13075                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13076             }
13077             
13078             if(this.labelsm > 0){
13079                 labelCfg.cls += ' col-sm-' + this.labelsm;
13080                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13081             }
13082             
13083             if(this.labelxs > 0){
13084                 labelCfg.cls += ' col-xs-' + this.labelxs;
13085                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13086             }
13087                 
13088                 
13089         } else if ( this.fieldLabel.length) {
13090 //                Roo.log(" label");
13091                  cfg.cn = [
13092                     {
13093                         tag : 'i',
13094                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13095                         tooltip : 'This field is required'
13096                     },
13097                     {
13098                         tag: 'label',
13099                         //cls : 'input-group-addon',
13100                         html : this.fieldLabel
13101                     },
13102                     combobox
13103                 ];
13104                 
13105                 if(this.indicatorpos == 'right'){
13106                     cfg.cn = [
13107                         {
13108                             tag: 'label',
13109                             //cls : 'input-group-addon',
13110                             html : this.fieldLabel
13111                         },
13112                         {
13113                             tag : 'i',
13114                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13115                             tooltip : 'This field is required'
13116                         },
13117                         combobox
13118                     ];
13119                     
13120                 }
13121
13122         } else {
13123             
13124 //                Roo.log(" no label && no align");
13125                 cfg = combobox
13126                      
13127                 
13128         }
13129          
13130         var settings=this;
13131         ['xs','sm','md','lg'].map(function(size){
13132             if (settings[size]) {
13133                 cfg.cls += ' col-' + size + '-' + settings[size];
13134             }
13135         });
13136         
13137         return cfg;
13138         
13139     },
13140     
13141     _initEventsCalled : false,
13142     
13143     // private
13144     initEvents: function()
13145     {   
13146         if (this._initEventsCalled) { // as we call render... prevent looping...
13147             return;
13148         }
13149         this._initEventsCalled = true;
13150         
13151         if (!this.store) {
13152             throw "can not find store for combo";
13153         }
13154         
13155         this.indicator = this.indicatorEl();
13156         
13157         this.store = Roo.factory(this.store, Roo.data);
13158         this.store.parent = this;
13159         
13160         // if we are building from html. then this element is so complex, that we can not really
13161         // use the rendered HTML.
13162         // so we have to trash and replace the previous code.
13163         if (Roo.XComponent.build_from_html) {
13164             // remove this element....
13165             var e = this.el.dom, k=0;
13166             while (e ) { e = e.previousSibling;  ++k;}
13167
13168             this.el.remove();
13169             
13170             this.el=false;
13171             this.rendered = false;
13172             
13173             this.render(this.parent().getChildContainer(true), k);
13174         }
13175         
13176         if(Roo.isIOS && this.useNativeIOS){
13177             this.initIOSView();
13178             return;
13179         }
13180         
13181         /*
13182          * Touch Devices
13183          */
13184         
13185         if(Roo.isTouch && this.mobileTouchView){
13186             this.initTouchView();
13187             return;
13188         }
13189         
13190         if(this.tickable){
13191             this.initTickableEvents();
13192             return;
13193         }
13194         
13195         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13196         
13197         if(this.hiddenName){
13198             
13199             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13200             
13201             this.hiddenField.dom.value =
13202                 this.hiddenValue !== undefined ? this.hiddenValue :
13203                 this.value !== undefined ? this.value : '';
13204
13205             // prevent input submission
13206             this.el.dom.removeAttribute('name');
13207             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13208              
13209              
13210         }
13211         //if(Roo.isGecko){
13212         //    this.el.dom.setAttribute('autocomplete', 'off');
13213         //}
13214         
13215         var cls = 'x-combo-list';
13216         
13217         //this.list = new Roo.Layer({
13218         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13219         //});
13220         
13221         var _this = this;
13222         
13223         (function(){
13224             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13225             _this.list.setWidth(lw);
13226         }).defer(100);
13227         
13228         this.list.on('mouseover', this.onViewOver, this);
13229         this.list.on('mousemove', this.onViewMove, this);
13230         this.list.on('scroll', this.onViewScroll, this);
13231         
13232         /*
13233         this.list.swallowEvent('mousewheel');
13234         this.assetHeight = 0;
13235
13236         if(this.title){
13237             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13238             this.assetHeight += this.header.getHeight();
13239         }
13240
13241         this.innerList = this.list.createChild({cls:cls+'-inner'});
13242         this.innerList.on('mouseover', this.onViewOver, this);
13243         this.innerList.on('mousemove', this.onViewMove, this);
13244         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13245         
13246         if(this.allowBlank && !this.pageSize && !this.disableClear){
13247             this.footer = this.list.createChild({cls:cls+'-ft'});
13248             this.pageTb = new Roo.Toolbar(this.footer);
13249            
13250         }
13251         if(this.pageSize){
13252             this.footer = this.list.createChild({cls:cls+'-ft'});
13253             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13254                     {pageSize: this.pageSize});
13255             
13256         }
13257         
13258         if (this.pageTb && this.allowBlank && !this.disableClear) {
13259             var _this = this;
13260             this.pageTb.add(new Roo.Toolbar.Fill(), {
13261                 cls: 'x-btn-icon x-btn-clear',
13262                 text: '&#160;',
13263                 handler: function()
13264                 {
13265                     _this.collapse();
13266                     _this.clearValue();
13267                     _this.onSelect(false, -1);
13268                 }
13269             });
13270         }
13271         if (this.footer) {
13272             this.assetHeight += this.footer.getHeight();
13273         }
13274         */
13275             
13276         if(!this.tpl){
13277             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13278         }
13279
13280         this.view = new Roo.View(this.list, this.tpl, {
13281             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13282         });
13283         //this.view.wrapEl.setDisplayed(false);
13284         this.view.on('click', this.onViewClick, this);
13285         
13286         
13287         this.store.on('beforeload', this.onBeforeLoad, this);
13288         this.store.on('load', this.onLoad, this);
13289         this.store.on('loadexception', this.onLoadException, this);
13290         /*
13291         if(this.resizable){
13292             this.resizer = new Roo.Resizable(this.list,  {
13293                pinned:true, handles:'se'
13294             });
13295             this.resizer.on('resize', function(r, w, h){
13296                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13297                 this.listWidth = w;
13298                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13299                 this.restrictHeight();
13300             }, this);
13301             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13302         }
13303         */
13304         if(!this.editable){
13305             this.editable = true;
13306             this.setEditable(false);
13307         }
13308         
13309         /*
13310         
13311         if (typeof(this.events.add.listeners) != 'undefined') {
13312             
13313             this.addicon = this.wrap.createChild(
13314                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13315        
13316             this.addicon.on('click', function(e) {
13317                 this.fireEvent('add', this);
13318             }, this);
13319         }
13320         if (typeof(this.events.edit.listeners) != 'undefined') {
13321             
13322             this.editicon = this.wrap.createChild(
13323                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13324             if (this.addicon) {
13325                 this.editicon.setStyle('margin-left', '40px');
13326             }
13327             this.editicon.on('click', function(e) {
13328                 
13329                 // we fire even  if inothing is selected..
13330                 this.fireEvent('edit', this, this.lastData );
13331                 
13332             }, this);
13333         }
13334         */
13335         
13336         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13337             "up" : function(e){
13338                 this.inKeyMode = true;
13339                 this.selectPrev();
13340             },
13341
13342             "down" : function(e){
13343                 if(!this.isExpanded()){
13344                     this.onTriggerClick();
13345                 }else{
13346                     this.inKeyMode = true;
13347                     this.selectNext();
13348                 }
13349             },
13350
13351             "enter" : function(e){
13352 //                this.onViewClick();
13353                 //return true;
13354                 this.collapse();
13355                 
13356                 if(this.fireEvent("specialkey", this, e)){
13357                     this.onViewClick(false);
13358                 }
13359                 
13360                 return true;
13361             },
13362
13363             "esc" : function(e){
13364                 this.collapse();
13365             },
13366
13367             "tab" : function(e){
13368                 this.collapse();
13369                 
13370                 if(this.fireEvent("specialkey", this, e)){
13371                     this.onViewClick(false);
13372                 }
13373                 
13374                 return true;
13375             },
13376
13377             scope : this,
13378
13379             doRelay : function(foo, bar, hname){
13380                 if(hname == 'down' || this.scope.isExpanded()){
13381                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13382                 }
13383                 return true;
13384             },
13385
13386             forceKeyDown: true
13387         });
13388         
13389         
13390         this.queryDelay = Math.max(this.queryDelay || 10,
13391                 this.mode == 'local' ? 10 : 250);
13392         
13393         
13394         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13395         
13396         if(this.typeAhead){
13397             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13398         }
13399         if(this.editable !== false){
13400             this.inputEl().on("keyup", this.onKeyUp, this);
13401         }
13402         if(this.forceSelection){
13403             this.inputEl().on('blur', this.doForce, this);
13404         }
13405         
13406         if(this.multiple){
13407             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13408             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13409         }
13410     },
13411     
13412     initTickableEvents: function()
13413     {   
13414         this.createList();
13415         
13416         if(this.hiddenName){
13417             
13418             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13419             
13420             this.hiddenField.dom.value =
13421                 this.hiddenValue !== undefined ? this.hiddenValue :
13422                 this.value !== undefined ? this.value : '';
13423
13424             // prevent input submission
13425             this.el.dom.removeAttribute('name');
13426             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13427              
13428              
13429         }
13430         
13431 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13432         
13433         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13434         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13435         if(this.triggerList){
13436             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13437         }
13438          
13439         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13440         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13441         
13442         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13443         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13444         
13445         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13446         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13447         
13448         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13449         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13450         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13451         
13452         this.okBtn.hide();
13453         this.cancelBtn.hide();
13454         
13455         var _this = this;
13456         
13457         (function(){
13458             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13459             _this.list.setWidth(lw);
13460         }).defer(100);
13461         
13462         this.list.on('mouseover', this.onViewOver, this);
13463         this.list.on('mousemove', this.onViewMove, this);
13464         
13465         this.list.on('scroll', this.onViewScroll, this);
13466         
13467         if(!this.tpl){
13468             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>';
13469         }
13470
13471         this.view = new Roo.View(this.list, this.tpl, {
13472             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13473         });
13474         
13475         //this.view.wrapEl.setDisplayed(false);
13476         this.view.on('click', this.onViewClick, this);
13477         
13478         
13479         
13480         this.store.on('beforeload', this.onBeforeLoad, this);
13481         this.store.on('load', this.onLoad, this);
13482         this.store.on('loadexception', this.onLoadException, this);
13483         
13484         if(this.editable){
13485             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13486                 "up" : function(e){
13487                     this.inKeyMode = true;
13488                     this.selectPrev();
13489                 },
13490
13491                 "down" : function(e){
13492                     this.inKeyMode = true;
13493                     this.selectNext();
13494                 },
13495
13496                 "enter" : function(e){
13497                     if(this.fireEvent("specialkey", this, e)){
13498                         this.onViewClick(false);
13499                     }
13500                     
13501                     return true;
13502                 },
13503
13504                 "esc" : function(e){
13505                     this.onTickableFooterButtonClick(e, false, false);
13506                 },
13507
13508                 "tab" : function(e){
13509                     this.fireEvent("specialkey", this, e);
13510                     
13511                     this.onTickableFooterButtonClick(e, false, false);
13512                     
13513                     return true;
13514                 },
13515
13516                 scope : this,
13517
13518                 doRelay : function(e, fn, key){
13519                     if(this.scope.isExpanded()){
13520                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13521                     }
13522                     return true;
13523                 },
13524
13525                 forceKeyDown: true
13526             });
13527         }
13528         
13529         this.queryDelay = Math.max(this.queryDelay || 10,
13530                 this.mode == 'local' ? 10 : 250);
13531         
13532         
13533         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13534         
13535         if(this.typeAhead){
13536             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13537         }
13538         
13539         if(this.editable !== false){
13540             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13541         }
13542         
13543         this.indicator = this.indicatorEl();
13544         
13545         if(this.indicator){
13546             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13547             this.indicator.hide();
13548         }
13549         
13550     },
13551
13552     onDestroy : function(){
13553         if(this.view){
13554             this.view.setStore(null);
13555             this.view.el.removeAllListeners();
13556             this.view.el.remove();
13557             this.view.purgeListeners();
13558         }
13559         if(this.list){
13560             this.list.dom.innerHTML  = '';
13561         }
13562         
13563         if(this.store){
13564             this.store.un('beforeload', this.onBeforeLoad, this);
13565             this.store.un('load', this.onLoad, this);
13566             this.store.un('loadexception', this.onLoadException, this);
13567         }
13568         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13569     },
13570
13571     // private
13572     fireKey : function(e){
13573         if(e.isNavKeyPress() && !this.list.isVisible()){
13574             this.fireEvent("specialkey", this, e);
13575         }
13576     },
13577
13578     // private
13579     onResize: function(w, h){
13580 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13581 //        
13582 //        if(typeof w != 'number'){
13583 //            // we do not handle it!?!?
13584 //            return;
13585 //        }
13586 //        var tw = this.trigger.getWidth();
13587 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13588 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13589 //        var x = w - tw;
13590 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13591 //            
13592 //        //this.trigger.setStyle('left', x+'px');
13593 //        
13594 //        if(this.list && this.listWidth === undefined){
13595 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13596 //            this.list.setWidth(lw);
13597 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13598 //        }
13599         
13600     
13601         
13602     },
13603
13604     /**
13605      * Allow or prevent the user from directly editing the field text.  If false is passed,
13606      * the user will only be able to select from the items defined in the dropdown list.  This method
13607      * is the runtime equivalent of setting the 'editable' config option at config time.
13608      * @param {Boolean} value True to allow the user to directly edit the field text
13609      */
13610     setEditable : function(value){
13611         if(value == this.editable){
13612             return;
13613         }
13614         this.editable = value;
13615         if(!value){
13616             this.inputEl().dom.setAttribute('readOnly', true);
13617             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13618             this.inputEl().addClass('x-combo-noedit');
13619         }else{
13620             this.inputEl().dom.setAttribute('readOnly', false);
13621             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13622             this.inputEl().removeClass('x-combo-noedit');
13623         }
13624     },
13625
13626     // private
13627     
13628     onBeforeLoad : function(combo,opts){
13629         if(!this.hasFocus){
13630             return;
13631         }
13632          if (!opts.add) {
13633             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13634          }
13635         this.restrictHeight();
13636         this.selectedIndex = -1;
13637     },
13638
13639     // private
13640     onLoad : function(){
13641         
13642         this.hasQuery = false;
13643         
13644         if(!this.hasFocus){
13645             return;
13646         }
13647         
13648         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13649             this.loading.hide();
13650         }
13651         
13652         if(this.store.getCount() > 0){
13653             
13654             this.expand();
13655             this.restrictHeight();
13656             if(this.lastQuery == this.allQuery){
13657                 if(this.editable && !this.tickable){
13658                     this.inputEl().dom.select();
13659                 }
13660                 
13661                 if(
13662                     !this.selectByValue(this.value, true) &&
13663                     this.autoFocus && 
13664                     (
13665                         !this.store.lastOptions ||
13666                         typeof(this.store.lastOptions.add) == 'undefined' || 
13667                         this.store.lastOptions.add != true
13668                     )
13669                 ){
13670                     this.select(0, true);
13671                 }
13672             }else{
13673                 if(this.autoFocus){
13674                     this.selectNext();
13675                 }
13676                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13677                     this.taTask.delay(this.typeAheadDelay);
13678                 }
13679             }
13680         }else{
13681             this.onEmptyResults();
13682         }
13683         
13684         //this.el.focus();
13685     },
13686     // private
13687     onLoadException : function()
13688     {
13689         this.hasQuery = false;
13690         
13691         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13692             this.loading.hide();
13693         }
13694         
13695         if(this.tickable && this.editable){
13696             return;
13697         }
13698         
13699         this.collapse();
13700         // only causes errors at present
13701         //Roo.log(this.store.reader.jsonData);
13702         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13703             // fixme
13704             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13705         //}
13706         
13707         
13708     },
13709     // private
13710     onTypeAhead : function(){
13711         if(this.store.getCount() > 0){
13712             var r = this.store.getAt(0);
13713             var newValue = r.data[this.displayField];
13714             var len = newValue.length;
13715             var selStart = this.getRawValue().length;
13716             
13717             if(selStart != len){
13718                 this.setRawValue(newValue);
13719                 this.selectText(selStart, newValue.length);
13720             }
13721         }
13722     },
13723
13724     // private
13725     onSelect : function(record, index){
13726         
13727         if(this.fireEvent('beforeselect', this, record, index) !== false){
13728         
13729             this.setFromData(index > -1 ? record.data : false);
13730             
13731             this.collapse();
13732             this.fireEvent('select', this, record, index);
13733         }
13734     },
13735
13736     /**
13737      * Returns the currently selected field value or empty string if no value is set.
13738      * @return {String} value The selected value
13739      */
13740     getValue : function()
13741     {
13742         if(Roo.isIOS && this.useNativeIOS){
13743             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13744         }
13745         
13746         if(this.multiple){
13747             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13748         }
13749         
13750         if(this.valueField){
13751             return typeof this.value != 'undefined' ? this.value : '';
13752         }else{
13753             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13754         }
13755     },
13756     
13757     getRawValue : function()
13758     {
13759         if(Roo.isIOS && this.useNativeIOS){
13760             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13761         }
13762         
13763         var v = this.inputEl().getValue();
13764         
13765         return v;
13766     },
13767
13768     /**
13769      * Clears any text/value currently set in the field
13770      */
13771     clearValue : function(){
13772         
13773         if(this.hiddenField){
13774             this.hiddenField.dom.value = '';
13775         }
13776         this.value = '';
13777         this.setRawValue('');
13778         this.lastSelectionText = '';
13779         this.lastData = false;
13780         
13781         var close = this.closeTriggerEl();
13782         
13783         if(close){
13784             close.hide();
13785         }
13786         
13787         this.validate();
13788         
13789     },
13790
13791     /**
13792      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13793      * will be displayed in the field.  If the value does not match the data value of an existing item,
13794      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13795      * Otherwise the field will be blank (although the value will still be set).
13796      * @param {String} value The value to match
13797      */
13798     setValue : function(v)
13799     {
13800         if(Roo.isIOS && this.useNativeIOS){
13801             this.setIOSValue(v);
13802             return;
13803         }
13804         
13805         if(this.multiple){
13806             this.syncValue();
13807             return;
13808         }
13809         
13810         var text = v;
13811         if(this.valueField){
13812             var r = this.findRecord(this.valueField, v);
13813             if(r){
13814                 text = r.data[this.displayField];
13815             }else if(this.valueNotFoundText !== undefined){
13816                 text = this.valueNotFoundText;
13817             }
13818         }
13819         this.lastSelectionText = text;
13820         if(this.hiddenField){
13821             this.hiddenField.dom.value = v;
13822         }
13823         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13824         this.value = v;
13825         
13826         var close = this.closeTriggerEl();
13827         
13828         if(close){
13829             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13830         }
13831         
13832         this.validate();
13833     },
13834     /**
13835      * @property {Object} the last set data for the element
13836      */
13837     
13838     lastData : false,
13839     /**
13840      * Sets the value of the field based on a object which is related to the record format for the store.
13841      * @param {Object} value the value to set as. or false on reset?
13842      */
13843     setFromData : function(o){
13844         
13845         if(this.multiple){
13846             this.addItem(o);
13847             return;
13848         }
13849             
13850         var dv = ''; // display value
13851         var vv = ''; // value value..
13852         this.lastData = o;
13853         if (this.displayField) {
13854             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13855         } else {
13856             // this is an error condition!!!
13857             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13858         }
13859         
13860         if(this.valueField){
13861             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13862         }
13863         
13864         var close = this.closeTriggerEl();
13865         
13866         if(close){
13867             if(dv.length || vv * 1 > 0){
13868                 close.show() ;
13869                 this.blockFocus=true;
13870             } else {
13871                 close.hide();
13872             }             
13873         }
13874         
13875         if(this.hiddenField){
13876             this.hiddenField.dom.value = vv;
13877             
13878             this.lastSelectionText = dv;
13879             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13880             this.value = vv;
13881             return;
13882         }
13883         // no hidden field.. - we store the value in 'value', but still display
13884         // display field!!!!
13885         this.lastSelectionText = dv;
13886         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13887         this.value = vv;
13888         
13889         
13890         
13891     },
13892     // private
13893     reset : function(){
13894         // overridden so that last data is reset..
13895         
13896         if(this.multiple){
13897             this.clearItem();
13898             return;
13899         }
13900         
13901         this.setValue(this.originalValue);
13902         //this.clearInvalid();
13903         this.lastData = false;
13904         if (this.view) {
13905             this.view.clearSelections();
13906         }
13907         
13908         this.validate();
13909     },
13910     // private
13911     findRecord : function(prop, value){
13912         var record;
13913         if(this.store.getCount() > 0){
13914             this.store.each(function(r){
13915                 if(r.data[prop] == value){
13916                     record = r;
13917                     return false;
13918                 }
13919                 return true;
13920             });
13921         }
13922         return record;
13923     },
13924     
13925     getName: function()
13926     {
13927         // returns hidden if it's set..
13928         if (!this.rendered) {return ''};
13929         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13930         
13931     },
13932     // private
13933     onViewMove : function(e, t){
13934         this.inKeyMode = false;
13935     },
13936
13937     // private
13938     onViewOver : function(e, t){
13939         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13940             return;
13941         }
13942         var item = this.view.findItemFromChild(t);
13943         
13944         if(item){
13945             var index = this.view.indexOf(item);
13946             this.select(index, false);
13947         }
13948     },
13949
13950     // private
13951     onViewClick : function(view, doFocus, el, e)
13952     {
13953         var index = this.view.getSelectedIndexes()[0];
13954         
13955         var r = this.store.getAt(index);
13956         
13957         if(this.tickable){
13958             
13959             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13960                 return;
13961             }
13962             
13963             var rm = false;
13964             var _this = this;
13965             
13966             Roo.each(this.tickItems, function(v,k){
13967                 
13968                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13969                     Roo.log(v);
13970                     _this.tickItems.splice(k, 1);
13971                     
13972                     if(typeof(e) == 'undefined' && view == false){
13973                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13974                     }
13975                     
13976                     rm = true;
13977                     return;
13978                 }
13979             });
13980             
13981             if(rm){
13982                 return;
13983             }
13984             
13985             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13986                 this.tickItems.push(r.data);
13987             }
13988             
13989             if(typeof(e) == 'undefined' && view == false){
13990                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13991             }
13992                     
13993             return;
13994         }
13995         
13996         if(r){
13997             this.onSelect(r, index);
13998         }
13999         if(doFocus !== false && !this.blockFocus){
14000             this.inputEl().focus();
14001         }
14002     },
14003
14004     // private
14005     restrictHeight : function(){
14006         //this.innerList.dom.style.height = '';
14007         //var inner = this.innerList.dom;
14008         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14009         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14010         //this.list.beginUpdate();
14011         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14012         this.list.alignTo(this.inputEl(), this.listAlign);
14013         this.list.alignTo(this.inputEl(), this.listAlign);
14014         //this.list.endUpdate();
14015     },
14016
14017     // private
14018     onEmptyResults : function(){
14019         
14020         if(this.tickable && this.editable){
14021             this.hasFocus = false;
14022             this.restrictHeight();
14023             return;
14024         }
14025         
14026         this.collapse();
14027     },
14028
14029     /**
14030      * Returns true if the dropdown list is expanded, else false.
14031      */
14032     isExpanded : function(){
14033         return this.list.isVisible();
14034     },
14035
14036     /**
14037      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14038      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14039      * @param {String} value The data value of the item to select
14040      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14041      * selected item if it is not currently in view (defaults to true)
14042      * @return {Boolean} True if the value matched an item in the list, else false
14043      */
14044     selectByValue : function(v, scrollIntoView){
14045         if(v !== undefined && v !== null){
14046             var r = this.findRecord(this.valueField || this.displayField, v);
14047             if(r){
14048                 this.select(this.store.indexOf(r), scrollIntoView);
14049                 return true;
14050             }
14051         }
14052         return false;
14053     },
14054
14055     /**
14056      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14057      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14058      * @param {Number} index The zero-based index of the list item to select
14059      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14060      * selected item if it is not currently in view (defaults to true)
14061      */
14062     select : function(index, scrollIntoView){
14063         this.selectedIndex = index;
14064         this.view.select(index);
14065         if(scrollIntoView !== false){
14066             var el = this.view.getNode(index);
14067             /*
14068              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14069              */
14070             if(el){
14071                 this.list.scrollChildIntoView(el, false);
14072             }
14073         }
14074     },
14075
14076     // private
14077     selectNext : function(){
14078         var ct = this.store.getCount();
14079         if(ct > 0){
14080             if(this.selectedIndex == -1){
14081                 this.select(0);
14082             }else if(this.selectedIndex < ct-1){
14083                 this.select(this.selectedIndex+1);
14084             }
14085         }
14086     },
14087
14088     // private
14089     selectPrev : function(){
14090         var ct = this.store.getCount();
14091         if(ct > 0){
14092             if(this.selectedIndex == -1){
14093                 this.select(0);
14094             }else if(this.selectedIndex != 0){
14095                 this.select(this.selectedIndex-1);
14096             }
14097         }
14098     },
14099
14100     // private
14101     onKeyUp : function(e){
14102         if(this.editable !== false && !e.isSpecialKey()){
14103             this.lastKey = e.getKey();
14104             this.dqTask.delay(this.queryDelay);
14105         }
14106     },
14107
14108     // private
14109     validateBlur : function(){
14110         return !this.list || !this.list.isVisible();   
14111     },
14112
14113     // private
14114     initQuery : function(){
14115         
14116         var v = this.getRawValue();
14117         
14118         if(this.tickable && this.editable){
14119             v = this.tickableInputEl().getValue();
14120         }
14121         
14122         this.doQuery(v);
14123     },
14124
14125     // private
14126     doForce : function(){
14127         if(this.inputEl().dom.value.length > 0){
14128             this.inputEl().dom.value =
14129                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14130              
14131         }
14132     },
14133
14134     /**
14135      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14136      * query allowing the query action to be canceled if needed.
14137      * @param {String} query The SQL query to execute
14138      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14139      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14140      * saved in the current store (defaults to false)
14141      */
14142     doQuery : function(q, forceAll){
14143         
14144         if(q === undefined || q === null){
14145             q = '';
14146         }
14147         var qe = {
14148             query: q,
14149             forceAll: forceAll,
14150             combo: this,
14151             cancel:false
14152         };
14153         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14154             return false;
14155         }
14156         q = qe.query;
14157         
14158         forceAll = qe.forceAll;
14159         if(forceAll === true || (q.length >= this.minChars)){
14160             
14161             this.hasQuery = true;
14162             
14163             if(this.lastQuery != q || this.alwaysQuery){
14164                 this.lastQuery = q;
14165                 if(this.mode == 'local'){
14166                     this.selectedIndex = -1;
14167                     if(forceAll){
14168                         this.store.clearFilter();
14169                     }else{
14170                         
14171                         if(this.specialFilter){
14172                             this.fireEvent('specialfilter', this);
14173                             this.onLoad();
14174                             return;
14175                         }
14176                         
14177                         this.store.filter(this.displayField, q);
14178                     }
14179                     
14180                     this.store.fireEvent("datachanged", this.store);
14181                     
14182                     this.onLoad();
14183                     
14184                     
14185                 }else{
14186                     
14187                     this.store.baseParams[this.queryParam] = q;
14188                     
14189                     var options = {params : this.getParams(q)};
14190                     
14191                     if(this.loadNext){
14192                         options.add = true;
14193                         options.params.start = this.page * this.pageSize;
14194                     }
14195                     
14196                     this.store.load(options);
14197                     
14198                     /*
14199                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14200                      *  we should expand the list on onLoad
14201                      *  so command out it
14202                      */
14203 //                    this.expand();
14204                 }
14205             }else{
14206                 this.selectedIndex = -1;
14207                 this.onLoad();   
14208             }
14209         }
14210         
14211         this.loadNext = false;
14212     },
14213     
14214     // private
14215     getParams : function(q){
14216         var p = {};
14217         //p[this.queryParam] = q;
14218         
14219         if(this.pageSize){
14220             p.start = 0;
14221             p.limit = this.pageSize;
14222         }
14223         return p;
14224     },
14225
14226     /**
14227      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14228      */
14229     collapse : function(){
14230         if(!this.isExpanded()){
14231             return;
14232         }
14233         
14234         this.list.hide();
14235         
14236         this.hasFocus = false;
14237         
14238         if(this.tickable){
14239             this.okBtn.hide();
14240             this.cancelBtn.hide();
14241             this.trigger.show();
14242             
14243             if(this.editable){
14244                 this.tickableInputEl().dom.value = '';
14245                 this.tickableInputEl().blur();
14246             }
14247             
14248         }
14249         
14250         Roo.get(document).un('mousedown', this.collapseIf, this);
14251         Roo.get(document).un('mousewheel', this.collapseIf, this);
14252         if (!this.editable) {
14253             Roo.get(document).un('keydown', this.listKeyPress, this);
14254         }
14255         this.fireEvent('collapse', this);
14256         
14257         this.validate();
14258     },
14259
14260     // private
14261     collapseIf : function(e){
14262         var in_combo  = e.within(this.el);
14263         var in_list =  e.within(this.list);
14264         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14265         
14266         if (in_combo || in_list || is_list) {
14267             //e.stopPropagation();
14268             return;
14269         }
14270         
14271         if(this.tickable){
14272             this.onTickableFooterButtonClick(e, false, false);
14273         }
14274
14275         this.collapse();
14276         
14277     },
14278
14279     /**
14280      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14281      */
14282     expand : function(){
14283        
14284         if(this.isExpanded() || !this.hasFocus){
14285             return;
14286         }
14287         
14288         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14289         this.list.setWidth(lw);
14290         
14291         Roo.log('expand');
14292         
14293         this.list.show();
14294         
14295         this.restrictHeight();
14296         
14297         if(this.tickable){
14298             
14299             this.tickItems = Roo.apply([], this.item);
14300             
14301             this.okBtn.show();
14302             this.cancelBtn.show();
14303             this.trigger.hide();
14304             
14305             if(this.editable){
14306                 this.tickableInputEl().focus();
14307             }
14308             
14309         }
14310         
14311         Roo.get(document).on('mousedown', this.collapseIf, this);
14312         Roo.get(document).on('mousewheel', this.collapseIf, this);
14313         if (!this.editable) {
14314             Roo.get(document).on('keydown', this.listKeyPress, this);
14315         }
14316         
14317         this.fireEvent('expand', this);
14318     },
14319
14320     // private
14321     // Implements the default empty TriggerField.onTriggerClick function
14322     onTriggerClick : function(e)
14323     {
14324         Roo.log('trigger click');
14325         
14326         if(this.disabled || !this.triggerList){
14327             return;
14328         }
14329         
14330         this.page = 0;
14331         this.loadNext = false;
14332         
14333         if(this.isExpanded()){
14334             this.collapse();
14335             if (!this.blockFocus) {
14336                 this.inputEl().focus();
14337             }
14338             
14339         }else {
14340             this.hasFocus = true;
14341             if(this.triggerAction == 'all') {
14342                 this.doQuery(this.allQuery, true);
14343             } else {
14344                 this.doQuery(this.getRawValue());
14345             }
14346             if (!this.blockFocus) {
14347                 this.inputEl().focus();
14348             }
14349         }
14350     },
14351     
14352     onTickableTriggerClick : function(e)
14353     {
14354         if(this.disabled){
14355             return;
14356         }
14357         
14358         this.page = 0;
14359         this.loadNext = false;
14360         this.hasFocus = true;
14361         
14362         if(this.triggerAction == 'all') {
14363             this.doQuery(this.allQuery, true);
14364         } else {
14365             this.doQuery(this.getRawValue());
14366         }
14367     },
14368     
14369     onSearchFieldClick : function(e)
14370     {
14371         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14372             this.onTickableFooterButtonClick(e, false, false);
14373             return;
14374         }
14375         
14376         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14377             return;
14378         }
14379         
14380         this.page = 0;
14381         this.loadNext = false;
14382         this.hasFocus = true;
14383         
14384         if(this.triggerAction == 'all') {
14385             this.doQuery(this.allQuery, true);
14386         } else {
14387             this.doQuery(this.getRawValue());
14388         }
14389     },
14390     
14391     listKeyPress : function(e)
14392     {
14393         //Roo.log('listkeypress');
14394         // scroll to first matching element based on key pres..
14395         if (e.isSpecialKey()) {
14396             return false;
14397         }
14398         var k = String.fromCharCode(e.getKey()).toUpperCase();
14399         //Roo.log(k);
14400         var match  = false;
14401         var csel = this.view.getSelectedNodes();
14402         var cselitem = false;
14403         if (csel.length) {
14404             var ix = this.view.indexOf(csel[0]);
14405             cselitem  = this.store.getAt(ix);
14406             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14407                 cselitem = false;
14408             }
14409             
14410         }
14411         
14412         this.store.each(function(v) { 
14413             if (cselitem) {
14414                 // start at existing selection.
14415                 if (cselitem.id == v.id) {
14416                     cselitem = false;
14417                 }
14418                 return true;
14419             }
14420                 
14421             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14422                 match = this.store.indexOf(v);
14423                 return false;
14424             }
14425             return true;
14426         }, this);
14427         
14428         if (match === false) {
14429             return true; // no more action?
14430         }
14431         // scroll to?
14432         this.view.select(match);
14433         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14434         sn.scrollIntoView(sn.dom.parentNode, false);
14435     },
14436     
14437     onViewScroll : function(e, t){
14438         
14439         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){
14440             return;
14441         }
14442         
14443         this.hasQuery = true;
14444         
14445         this.loading = this.list.select('.loading', true).first();
14446         
14447         if(this.loading === null){
14448             this.list.createChild({
14449                 tag: 'div',
14450                 cls: 'loading roo-select2-more-results roo-select2-active',
14451                 html: 'Loading more results...'
14452             });
14453             
14454             this.loading = this.list.select('.loading', true).first();
14455             
14456             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14457             
14458             this.loading.hide();
14459         }
14460         
14461         this.loading.show();
14462         
14463         var _combo = this;
14464         
14465         this.page++;
14466         this.loadNext = true;
14467         
14468         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14469         
14470         return;
14471     },
14472     
14473     addItem : function(o)
14474     {   
14475         var dv = ''; // display value
14476         
14477         if (this.displayField) {
14478             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14479         } else {
14480             // this is an error condition!!!
14481             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14482         }
14483         
14484         if(!dv.length){
14485             return;
14486         }
14487         
14488         var choice = this.choices.createChild({
14489             tag: 'li',
14490             cls: 'roo-select2-search-choice',
14491             cn: [
14492                 {
14493                     tag: 'div',
14494                     html: dv
14495                 },
14496                 {
14497                     tag: 'a',
14498                     href: '#',
14499                     cls: 'roo-select2-search-choice-close fa fa-times',
14500                     tabindex: '-1'
14501                 }
14502             ]
14503             
14504         }, this.searchField);
14505         
14506         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14507         
14508         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14509         
14510         this.item.push(o);
14511         
14512         this.lastData = o;
14513         
14514         this.syncValue();
14515         
14516         this.inputEl().dom.value = '';
14517         
14518         this.validate();
14519     },
14520     
14521     onRemoveItem : function(e, _self, o)
14522     {
14523         e.preventDefault();
14524         
14525         this.lastItem = Roo.apply([], this.item);
14526         
14527         var index = this.item.indexOf(o.data) * 1;
14528         
14529         if( index < 0){
14530             Roo.log('not this item?!');
14531             return;
14532         }
14533         
14534         this.item.splice(index, 1);
14535         o.item.remove();
14536         
14537         this.syncValue();
14538         
14539         this.fireEvent('remove', this, e);
14540         
14541         this.validate();
14542         
14543     },
14544     
14545     syncValue : function()
14546     {
14547         if(!this.item.length){
14548             this.clearValue();
14549             return;
14550         }
14551             
14552         var value = [];
14553         var _this = this;
14554         Roo.each(this.item, function(i){
14555             if(_this.valueField){
14556                 value.push(i[_this.valueField]);
14557                 return;
14558             }
14559
14560             value.push(i);
14561         });
14562
14563         this.value = value.join(',');
14564
14565         if(this.hiddenField){
14566             this.hiddenField.dom.value = this.value;
14567         }
14568         
14569         this.store.fireEvent("datachanged", this.store);
14570         
14571         this.validate();
14572     },
14573     
14574     clearItem : function()
14575     {
14576         if(!this.multiple){
14577             return;
14578         }
14579         
14580         this.item = [];
14581         
14582         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14583            c.remove();
14584         });
14585         
14586         this.syncValue();
14587         
14588         this.validate();
14589         
14590         if(this.tickable && !Roo.isTouch){
14591             this.view.refresh();
14592         }
14593     },
14594     
14595     inputEl: function ()
14596     {
14597         if(Roo.isIOS && this.useNativeIOS){
14598             return this.el.select('select.roo-ios-select', true).first();
14599         }
14600         
14601         if(Roo.isTouch && this.mobileTouchView){
14602             return this.el.select('input.form-control',true).first();
14603         }
14604         
14605         if(this.tickable){
14606             return this.searchField;
14607         }
14608         
14609         return this.el.select('input.form-control',true).first();
14610     },
14611     
14612     onTickableFooterButtonClick : function(e, btn, el)
14613     {
14614         e.preventDefault();
14615         
14616         this.lastItem = Roo.apply([], this.item);
14617         
14618         if(btn && btn.name == 'cancel'){
14619             this.tickItems = Roo.apply([], this.item);
14620             this.collapse();
14621             return;
14622         }
14623         
14624         this.clearItem();
14625         
14626         var _this = this;
14627         
14628         Roo.each(this.tickItems, function(o){
14629             _this.addItem(o);
14630         });
14631         
14632         this.collapse();
14633         
14634     },
14635     
14636     validate : function()
14637     {
14638         var v = this.getRawValue();
14639         
14640         if(this.multiple){
14641             v = this.getValue();
14642         }
14643         
14644         if(this.disabled || this.allowBlank || v.length){
14645             this.markValid();
14646             return true;
14647         }
14648         
14649         this.markInvalid();
14650         return false;
14651     },
14652     
14653     tickableInputEl : function()
14654     {
14655         if(!this.tickable || !this.editable){
14656             return this.inputEl();
14657         }
14658         
14659         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14660     },
14661     
14662     
14663     getAutoCreateTouchView : function()
14664     {
14665         var id = Roo.id();
14666         
14667         var cfg = {
14668             cls: 'form-group' //input-group
14669         };
14670         
14671         var input =  {
14672             tag: 'input',
14673             id : id,
14674             type : this.inputType,
14675             cls : 'form-control x-combo-noedit',
14676             autocomplete: 'new-password',
14677             placeholder : this.placeholder || '',
14678             readonly : true
14679         };
14680         
14681         if (this.name) {
14682             input.name = this.name;
14683         }
14684         
14685         if (this.size) {
14686             input.cls += ' input-' + this.size;
14687         }
14688         
14689         if (this.disabled) {
14690             input.disabled = true;
14691         }
14692         
14693         var inputblock = {
14694             cls : '',
14695             cn : [
14696                 input
14697             ]
14698         };
14699         
14700         if(this.before){
14701             inputblock.cls += ' input-group';
14702             
14703             inputblock.cn.unshift({
14704                 tag :'span',
14705                 cls : 'input-group-addon',
14706                 html : this.before
14707             });
14708         }
14709         
14710         if(this.removable && !this.multiple){
14711             inputblock.cls += ' roo-removable';
14712             
14713             inputblock.cn.push({
14714                 tag: 'button',
14715                 html : 'x',
14716                 cls : 'roo-combo-removable-btn close'
14717             });
14718         }
14719
14720         if(this.hasFeedback && !this.allowBlank){
14721             
14722             inputblock.cls += ' has-feedback';
14723             
14724             inputblock.cn.push({
14725                 tag: 'span',
14726                 cls: 'glyphicon form-control-feedback'
14727             });
14728             
14729         }
14730         
14731         if (this.after) {
14732             
14733             inputblock.cls += (this.before) ? '' : ' input-group';
14734             
14735             inputblock.cn.push({
14736                 tag :'span',
14737                 cls : 'input-group-addon',
14738                 html : this.after
14739             });
14740         }
14741
14742         var box = {
14743             tag: 'div',
14744             cn: [
14745                 {
14746                     tag: 'input',
14747                     type : 'hidden',
14748                     cls: 'form-hidden-field'
14749                 },
14750                 inputblock
14751             ]
14752             
14753         };
14754         
14755         if(this.multiple){
14756             box = {
14757                 tag: 'div',
14758                 cn: [
14759                     {
14760                         tag: 'input',
14761                         type : 'hidden',
14762                         cls: 'form-hidden-field'
14763                     },
14764                     {
14765                         tag: 'ul',
14766                         cls: 'roo-select2-choices',
14767                         cn:[
14768                             {
14769                                 tag: 'li',
14770                                 cls: 'roo-select2-search-field',
14771                                 cn: [
14772
14773                                     inputblock
14774                                 ]
14775                             }
14776                         ]
14777                     }
14778                 ]
14779             }
14780         };
14781         
14782         var combobox = {
14783             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14784             cn: [
14785                 box
14786             ]
14787         };
14788         
14789         if(!this.multiple && this.showToggleBtn){
14790             
14791             var caret = {
14792                         tag: 'span',
14793                         cls: 'caret'
14794             };
14795             
14796             if (this.caret != false) {
14797                 caret = {
14798                      tag: 'i',
14799                      cls: 'fa fa-' + this.caret
14800                 };
14801                 
14802             }
14803             
14804             combobox.cn.push({
14805                 tag :'span',
14806                 cls : 'input-group-addon btn dropdown-toggle',
14807                 cn : [
14808                     caret,
14809                     {
14810                         tag: 'span',
14811                         cls: 'combobox-clear',
14812                         cn  : [
14813                             {
14814                                 tag : 'i',
14815                                 cls: 'icon-remove'
14816                             }
14817                         ]
14818                     }
14819                 ]
14820
14821             })
14822         }
14823         
14824         if(this.multiple){
14825             combobox.cls += ' roo-select2-container-multi';
14826         }
14827         
14828         var align = this.labelAlign || this.parentLabelAlign();
14829         
14830         if (align ==='left' && this.fieldLabel.length) {
14831
14832             cfg.cn = [
14833                 {
14834                    tag : 'i',
14835                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14836                    tooltip : 'This field is required'
14837                 },
14838                 {
14839                     tag: 'label',
14840                     cls : 'control-label',
14841                     html : this.fieldLabel
14842
14843                 },
14844                 {
14845                     cls : '', 
14846                     cn: [
14847                         combobox
14848                     ]
14849                 }
14850             ];
14851             
14852             var labelCfg = cfg.cn[1];
14853             var contentCfg = cfg.cn[2];
14854             
14855
14856             if(this.indicatorpos == 'right'){
14857                 cfg.cn = [
14858                     {
14859                         tag: 'label',
14860                         'for' :  id,
14861                         cls : 'control-label',
14862                         cn : [
14863                             {
14864                                 tag : 'span',
14865                                 html : this.fieldLabel
14866                             },
14867                             {
14868                                 tag : 'i',
14869                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14870                                 tooltip : 'This field is required'
14871                             }
14872                         ]
14873                     },
14874                     {
14875                         cls : "",
14876                         cn: [
14877                             combobox
14878                         ]
14879                     }
14880
14881                 ];
14882                 
14883                 labelCfg = cfg.cn[0];
14884                 contentCfg = cfg.cn[1];
14885             }
14886             
14887            
14888             
14889             if(this.labelWidth > 12){
14890                 labelCfg.style = "width: " + this.labelWidth + 'px';
14891             }
14892             
14893             if(this.labelWidth < 13 && this.labelmd == 0){
14894                 this.labelmd = this.labelWidth;
14895             }
14896             
14897             if(this.labellg > 0){
14898                 labelCfg.cls += ' col-lg-' + this.labellg;
14899                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14900             }
14901             
14902             if(this.labelmd > 0){
14903                 labelCfg.cls += ' col-md-' + this.labelmd;
14904                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14905             }
14906             
14907             if(this.labelsm > 0){
14908                 labelCfg.cls += ' col-sm-' + this.labelsm;
14909                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14910             }
14911             
14912             if(this.labelxs > 0){
14913                 labelCfg.cls += ' col-xs-' + this.labelxs;
14914                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14915             }
14916                 
14917                 
14918         } else if ( this.fieldLabel.length) {
14919             cfg.cn = [
14920                 {
14921                    tag : 'i',
14922                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14923                    tooltip : 'This field is required'
14924                 },
14925                 {
14926                     tag: 'label',
14927                     cls : 'control-label',
14928                     html : this.fieldLabel
14929
14930                 },
14931                 {
14932                     cls : '', 
14933                     cn: [
14934                         combobox
14935                     ]
14936                 }
14937             ];
14938             
14939             if(this.indicatorpos == 'right'){
14940                 cfg.cn = [
14941                     {
14942                         tag: 'label',
14943                         cls : 'control-label',
14944                         html : this.fieldLabel,
14945                         cn : [
14946                             {
14947                                tag : 'i',
14948                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14949                                tooltip : 'This field is required'
14950                             }
14951                         ]
14952                     },
14953                     {
14954                         cls : '', 
14955                         cn: [
14956                             combobox
14957                         ]
14958                     }
14959                 ];
14960             }
14961         } else {
14962             cfg.cn = combobox;    
14963         }
14964         
14965         
14966         var settings = this;
14967         
14968         ['xs','sm','md','lg'].map(function(size){
14969             if (settings[size]) {
14970                 cfg.cls += ' col-' + size + '-' + settings[size];
14971             }
14972         });
14973         
14974         return cfg;
14975     },
14976     
14977     initTouchView : function()
14978     {
14979         this.renderTouchView();
14980         
14981         this.touchViewEl.on('scroll', function(){
14982             this.el.dom.scrollTop = 0;
14983         }, this);
14984         
14985         this.originalValue = this.getValue();
14986         
14987         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14988         
14989         this.inputEl().on("click", this.showTouchView, this);
14990         if (this.triggerEl) {
14991             this.triggerEl.on("click", this.showTouchView, this);
14992         }
14993         
14994         
14995         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14996         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14997         
14998         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14999         
15000         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15001         this.store.on('load', this.onTouchViewLoad, this);
15002         this.store.on('loadexception', this.onTouchViewLoadException, this);
15003         
15004         if(this.hiddenName){
15005             
15006             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15007             
15008             this.hiddenField.dom.value =
15009                 this.hiddenValue !== undefined ? this.hiddenValue :
15010                 this.value !== undefined ? this.value : '';
15011         
15012             this.el.dom.removeAttribute('name');
15013             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15014         }
15015         
15016         if(this.multiple){
15017             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15018             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15019         }
15020         
15021         if(this.removable && !this.multiple){
15022             var close = this.closeTriggerEl();
15023             if(close){
15024                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15025                 close.on('click', this.removeBtnClick, this, close);
15026             }
15027         }
15028         /*
15029          * fix the bug in Safari iOS8
15030          */
15031         this.inputEl().on("focus", function(e){
15032             document.activeElement.blur();
15033         }, this);
15034         
15035         return;
15036         
15037         
15038     },
15039     
15040     renderTouchView : function()
15041     {
15042         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15043         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15044         
15045         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15046         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15047         
15048         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15049         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15050         this.touchViewBodyEl.setStyle('overflow', 'auto');
15051         
15052         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15053         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15054         
15055         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15056         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15057         
15058     },
15059     
15060     showTouchView : function()
15061     {
15062         if(this.disabled){
15063             return;
15064         }
15065         
15066         this.touchViewHeaderEl.hide();
15067
15068         if(this.modalTitle.length){
15069             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15070             this.touchViewHeaderEl.show();
15071         }
15072
15073         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15074         this.touchViewEl.show();
15075
15076         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15077         
15078         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15079         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15080
15081         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15082
15083         if(this.modalTitle.length){
15084             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15085         }
15086         
15087         this.touchViewBodyEl.setHeight(bodyHeight);
15088
15089         if(this.animate){
15090             var _this = this;
15091             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15092         }else{
15093             this.touchViewEl.addClass('in');
15094         }
15095
15096         this.doTouchViewQuery();
15097         
15098     },
15099     
15100     hideTouchView : function()
15101     {
15102         this.touchViewEl.removeClass('in');
15103
15104         if(this.animate){
15105             var _this = this;
15106             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15107         }else{
15108             this.touchViewEl.setStyle('display', 'none');
15109         }
15110         
15111     },
15112     
15113     setTouchViewValue : function()
15114     {
15115         if(this.multiple){
15116             this.clearItem();
15117         
15118             var _this = this;
15119
15120             Roo.each(this.tickItems, function(o){
15121                 this.addItem(o);
15122             }, this);
15123         }
15124         
15125         this.hideTouchView();
15126     },
15127     
15128     doTouchViewQuery : function()
15129     {
15130         var qe = {
15131             query: '',
15132             forceAll: true,
15133             combo: this,
15134             cancel:false
15135         };
15136         
15137         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15138             return false;
15139         }
15140         
15141         if(!this.alwaysQuery || this.mode == 'local'){
15142             this.onTouchViewLoad();
15143             return;
15144         }
15145         
15146         this.store.load();
15147     },
15148     
15149     onTouchViewBeforeLoad : function(combo,opts)
15150     {
15151         return;
15152     },
15153
15154     // private
15155     onTouchViewLoad : function()
15156     {
15157         if(this.store.getCount() < 1){
15158             this.onTouchViewEmptyResults();
15159             return;
15160         }
15161         
15162         this.clearTouchView();
15163         
15164         var rawValue = this.getRawValue();
15165         
15166         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15167         
15168         this.tickItems = [];
15169         
15170         this.store.data.each(function(d, rowIndex){
15171             var row = this.touchViewListGroup.createChild(template);
15172             
15173             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15174                 row.addClass(d.data.cls);
15175             }
15176             
15177             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15178                 var cfg = {
15179                     data : d.data,
15180                     html : d.data[this.displayField]
15181                 };
15182                 
15183                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15184                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15185                 }
15186             }
15187             row.removeClass('selected');
15188             if(!this.multiple && this.valueField &&
15189                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15190             {
15191                 // radio buttons..
15192                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15193                 row.addClass('selected');
15194             }
15195             
15196             if(this.multiple && this.valueField &&
15197                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15198             {
15199                 
15200                 // checkboxes...
15201                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15202                 this.tickItems.push(d.data);
15203             }
15204             
15205             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15206             
15207         }, this);
15208         
15209         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15210         
15211         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15212
15213         if(this.modalTitle.length){
15214             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15215         }
15216
15217         var listHeight = this.touchViewListGroup.getHeight();
15218         
15219         var _this = this;
15220         
15221         if(firstChecked && listHeight > bodyHeight){
15222             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15223         }
15224         
15225     },
15226     
15227     onTouchViewLoadException : function()
15228     {
15229         this.hideTouchView();
15230     },
15231     
15232     onTouchViewEmptyResults : function()
15233     {
15234         this.clearTouchView();
15235         
15236         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15237         
15238         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15239         
15240     },
15241     
15242     clearTouchView : function()
15243     {
15244         this.touchViewListGroup.dom.innerHTML = '';
15245     },
15246     
15247     onTouchViewClick : function(e, el, o)
15248     {
15249         e.preventDefault();
15250         
15251         var row = o.row;
15252         var rowIndex = o.rowIndex;
15253         
15254         var r = this.store.getAt(rowIndex);
15255         
15256         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15257             
15258             if(!this.multiple){
15259                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15260                     c.dom.removeAttribute('checked');
15261                 }, this);
15262
15263                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15264
15265                 this.setFromData(r.data);
15266
15267                 var close = this.closeTriggerEl();
15268
15269                 if(close){
15270                     close.show();
15271                 }
15272
15273                 this.hideTouchView();
15274
15275                 this.fireEvent('select', this, r, rowIndex);
15276
15277                 return;
15278             }
15279
15280             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15281                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15282                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15283                 return;
15284             }
15285
15286             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15287             this.addItem(r.data);
15288             this.tickItems.push(r.data);
15289         }
15290     },
15291     
15292     getAutoCreateNativeIOS : function()
15293     {
15294         var cfg = {
15295             cls: 'form-group' //input-group,
15296         };
15297         
15298         var combobox =  {
15299             tag: 'select',
15300             cls : 'roo-ios-select'
15301         };
15302         
15303         if (this.name) {
15304             combobox.name = this.name;
15305         }
15306         
15307         if (this.disabled) {
15308             combobox.disabled = true;
15309         }
15310         
15311         var settings = this;
15312         
15313         ['xs','sm','md','lg'].map(function(size){
15314             if (settings[size]) {
15315                 cfg.cls += ' col-' + size + '-' + settings[size];
15316             }
15317         });
15318         
15319         cfg.cn = combobox;
15320         
15321         return cfg;
15322         
15323     },
15324     
15325     initIOSView : function()
15326     {
15327         this.store.on('load', this.onIOSViewLoad, this);
15328         
15329         return;
15330     },
15331     
15332     onIOSViewLoad : function()
15333     {
15334         if(this.store.getCount() < 1){
15335             return;
15336         }
15337         
15338         this.clearIOSView();
15339         
15340         if(this.allowBlank) {
15341             
15342             var default_text = '-- SELECT --';
15343             
15344             if(this.placeholder.length){
15345                 default_text = this.placeholder;
15346             }
15347             
15348             if(this.emptyTitle.length){
15349                 default_text += ' - ' + this.emptyTitle + ' -';
15350             }
15351             
15352             var opt = this.inputEl().createChild({
15353                 tag: 'option',
15354                 value : 0,
15355                 html : default_text
15356             });
15357             
15358             var o = {};
15359             o[this.valueField] = 0;
15360             o[this.displayField] = default_text;
15361             
15362             this.ios_options.push({
15363                 data : o,
15364                 el : opt
15365             });
15366             
15367         }
15368         
15369         this.store.data.each(function(d, rowIndex){
15370             
15371             var html = '';
15372             
15373             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15374                 html = d.data[this.displayField];
15375             }
15376             
15377             var value = '';
15378             
15379             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15380                 value = d.data[this.valueField];
15381             }
15382             
15383             var option = {
15384                 tag: 'option',
15385                 value : value,
15386                 html : html
15387             };
15388             
15389             if(this.value == d.data[this.valueField]){
15390                 option['selected'] = true;
15391             }
15392             
15393             var opt = this.inputEl().createChild(option);
15394             
15395             this.ios_options.push({
15396                 data : d.data,
15397                 el : opt
15398             });
15399             
15400         }, this);
15401         
15402         this.inputEl().on('change', function(){
15403            this.fireEvent('select', this);
15404         }, this);
15405         
15406     },
15407     
15408     clearIOSView: function()
15409     {
15410         this.inputEl().dom.innerHTML = '';
15411         
15412         this.ios_options = [];
15413     },
15414     
15415     setIOSValue: function(v)
15416     {
15417         this.value = v;
15418         
15419         if(!this.ios_options){
15420             return;
15421         }
15422         
15423         Roo.each(this.ios_options, function(opts){
15424            
15425            opts.el.dom.removeAttribute('selected');
15426            
15427            if(opts.data[this.valueField] != v){
15428                return;
15429            }
15430            
15431            opts.el.dom.setAttribute('selected', true);
15432            
15433         }, this);
15434     }
15435
15436     /** 
15437     * @cfg {Boolean} grow 
15438     * @hide 
15439     */
15440     /** 
15441     * @cfg {Number} growMin 
15442     * @hide 
15443     */
15444     /** 
15445     * @cfg {Number} growMax 
15446     * @hide 
15447     */
15448     /**
15449      * @hide
15450      * @method autoSize
15451      */
15452 });
15453
15454 Roo.apply(Roo.bootstrap.ComboBox,  {
15455     
15456     header : {
15457         tag: 'div',
15458         cls: 'modal-header',
15459         cn: [
15460             {
15461                 tag: 'h4',
15462                 cls: 'modal-title'
15463             }
15464         ]
15465     },
15466     
15467     body : {
15468         tag: 'div',
15469         cls: 'modal-body',
15470         cn: [
15471             {
15472                 tag: 'ul',
15473                 cls: 'list-group'
15474             }
15475         ]
15476     },
15477     
15478     listItemRadio : {
15479         tag: 'li',
15480         cls: 'list-group-item',
15481         cn: [
15482             {
15483                 tag: 'span',
15484                 cls: 'roo-combobox-list-group-item-value'
15485             },
15486             {
15487                 tag: 'div',
15488                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15489                 cn: [
15490                     {
15491                         tag: 'input',
15492                         type: 'radio'
15493                     },
15494                     {
15495                         tag: 'label'
15496                     }
15497                 ]
15498             }
15499         ]
15500     },
15501     
15502     listItemCheckbox : {
15503         tag: 'li',
15504         cls: 'list-group-item',
15505         cn: [
15506             {
15507                 tag: 'span',
15508                 cls: 'roo-combobox-list-group-item-value'
15509             },
15510             {
15511                 tag: 'div',
15512                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15513                 cn: [
15514                     {
15515                         tag: 'input',
15516                         type: 'checkbox'
15517                     },
15518                     {
15519                         tag: 'label'
15520                     }
15521                 ]
15522             }
15523         ]
15524     },
15525     
15526     emptyResult : {
15527         tag: 'div',
15528         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15529     },
15530     
15531     footer : {
15532         tag: 'div',
15533         cls: 'modal-footer',
15534         cn: [
15535             {
15536                 tag: 'div',
15537                 cls: 'row',
15538                 cn: [
15539                     {
15540                         tag: 'div',
15541                         cls: 'col-xs-6 text-left',
15542                         cn: {
15543                             tag: 'button',
15544                             cls: 'btn btn-danger roo-touch-view-cancel',
15545                             html: 'Cancel'
15546                         }
15547                     },
15548                     {
15549                         tag: 'div',
15550                         cls: 'col-xs-6 text-right',
15551                         cn: {
15552                             tag: 'button',
15553                             cls: 'btn btn-success roo-touch-view-ok',
15554                             html: 'OK'
15555                         }
15556                     }
15557                 ]
15558             }
15559         ]
15560         
15561     }
15562 });
15563
15564 Roo.apply(Roo.bootstrap.ComboBox,  {
15565     
15566     touchViewTemplate : {
15567         tag: 'div',
15568         cls: 'modal fade roo-combobox-touch-view',
15569         cn: [
15570             {
15571                 tag: 'div',
15572                 cls: 'modal-dialog',
15573                 style : 'position:fixed', // we have to fix position....
15574                 cn: [
15575                     {
15576                         tag: 'div',
15577                         cls: 'modal-content',
15578                         cn: [
15579                             Roo.bootstrap.ComboBox.header,
15580                             Roo.bootstrap.ComboBox.body,
15581                             Roo.bootstrap.ComboBox.footer
15582                         ]
15583                     }
15584                 ]
15585             }
15586         ]
15587     }
15588 });/*
15589  * Based on:
15590  * Ext JS Library 1.1.1
15591  * Copyright(c) 2006-2007, Ext JS, LLC.
15592  *
15593  * Originally Released Under LGPL - original licence link has changed is not relivant.
15594  *
15595  * Fork - LGPL
15596  * <script type="text/javascript">
15597  */
15598
15599 /**
15600  * @class Roo.View
15601  * @extends Roo.util.Observable
15602  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15603  * This class also supports single and multi selection modes. <br>
15604  * Create a data model bound view:
15605  <pre><code>
15606  var store = new Roo.data.Store(...);
15607
15608  var view = new Roo.View({
15609     el : "my-element",
15610     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15611  
15612     singleSelect: true,
15613     selectedClass: "ydataview-selected",
15614     store: store
15615  });
15616
15617  // listen for node click?
15618  view.on("click", function(vw, index, node, e){
15619  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15620  });
15621
15622  // load XML data
15623  dataModel.load("foobar.xml");
15624  </code></pre>
15625  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15626  * <br><br>
15627  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15628  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15629  * 
15630  * Note: old style constructor is still suported (container, template, config)
15631  * 
15632  * @constructor
15633  * Create a new View
15634  * @param {Object} config The config object
15635  * 
15636  */
15637 Roo.View = function(config, depreciated_tpl, depreciated_config){
15638     
15639     this.parent = false;
15640     
15641     if (typeof(depreciated_tpl) == 'undefined') {
15642         // new way.. - universal constructor.
15643         Roo.apply(this, config);
15644         this.el  = Roo.get(this.el);
15645     } else {
15646         // old format..
15647         this.el  = Roo.get(config);
15648         this.tpl = depreciated_tpl;
15649         Roo.apply(this, depreciated_config);
15650     }
15651     this.wrapEl  = this.el.wrap().wrap();
15652     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15653     
15654     
15655     if(typeof(this.tpl) == "string"){
15656         this.tpl = new Roo.Template(this.tpl);
15657     } else {
15658         // support xtype ctors..
15659         this.tpl = new Roo.factory(this.tpl, Roo);
15660     }
15661     
15662     
15663     this.tpl.compile();
15664     
15665     /** @private */
15666     this.addEvents({
15667         /**
15668          * @event beforeclick
15669          * Fires before a click is processed. Returns false to cancel the default action.
15670          * @param {Roo.View} this
15671          * @param {Number} index The index of the target node
15672          * @param {HTMLElement} node The target node
15673          * @param {Roo.EventObject} e The raw event object
15674          */
15675             "beforeclick" : true,
15676         /**
15677          * @event click
15678          * Fires when a template node is clicked.
15679          * @param {Roo.View} this
15680          * @param {Number} index The index of the target node
15681          * @param {HTMLElement} node The target node
15682          * @param {Roo.EventObject} e The raw event object
15683          */
15684             "click" : true,
15685         /**
15686          * @event dblclick
15687          * Fires when a template node is double clicked.
15688          * @param {Roo.View} this
15689          * @param {Number} index The index of the target node
15690          * @param {HTMLElement} node The target node
15691          * @param {Roo.EventObject} e The raw event object
15692          */
15693             "dblclick" : true,
15694         /**
15695          * @event contextmenu
15696          * Fires when a template node is right clicked.
15697          * @param {Roo.View} this
15698          * @param {Number} index The index of the target node
15699          * @param {HTMLElement} node The target node
15700          * @param {Roo.EventObject} e The raw event object
15701          */
15702             "contextmenu" : true,
15703         /**
15704          * @event selectionchange
15705          * Fires when the selected nodes change.
15706          * @param {Roo.View} this
15707          * @param {Array} selections Array of the selected nodes
15708          */
15709             "selectionchange" : true,
15710     
15711         /**
15712          * @event beforeselect
15713          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15714          * @param {Roo.View} this
15715          * @param {HTMLElement} node The node to be selected
15716          * @param {Array} selections Array of currently selected nodes
15717          */
15718             "beforeselect" : true,
15719         /**
15720          * @event preparedata
15721          * Fires on every row to render, to allow you to change the data.
15722          * @param {Roo.View} this
15723          * @param {Object} data to be rendered (change this)
15724          */
15725           "preparedata" : true
15726           
15727           
15728         });
15729
15730
15731
15732     this.el.on({
15733         "click": this.onClick,
15734         "dblclick": this.onDblClick,
15735         "contextmenu": this.onContextMenu,
15736         scope:this
15737     });
15738
15739     this.selections = [];
15740     this.nodes = [];
15741     this.cmp = new Roo.CompositeElementLite([]);
15742     if(this.store){
15743         this.store = Roo.factory(this.store, Roo.data);
15744         this.setStore(this.store, true);
15745     }
15746     
15747     if ( this.footer && this.footer.xtype) {
15748            
15749          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15750         
15751         this.footer.dataSource = this.store;
15752         this.footer.container = fctr;
15753         this.footer = Roo.factory(this.footer, Roo);
15754         fctr.insertFirst(this.el);
15755         
15756         // this is a bit insane - as the paging toolbar seems to detach the el..
15757 //        dom.parentNode.parentNode.parentNode
15758          // they get detached?
15759     }
15760     
15761     
15762     Roo.View.superclass.constructor.call(this);
15763     
15764     
15765 };
15766
15767 Roo.extend(Roo.View, Roo.util.Observable, {
15768     
15769      /**
15770      * @cfg {Roo.data.Store} store Data store to load data from.
15771      */
15772     store : false,
15773     
15774     /**
15775      * @cfg {String|Roo.Element} el The container element.
15776      */
15777     el : '',
15778     
15779     /**
15780      * @cfg {String|Roo.Template} tpl The template used by this View 
15781      */
15782     tpl : false,
15783     /**
15784      * @cfg {String} dataName the named area of the template to use as the data area
15785      *                          Works with domtemplates roo-name="name"
15786      */
15787     dataName: false,
15788     /**
15789      * @cfg {String} selectedClass The css class to add to selected nodes
15790      */
15791     selectedClass : "x-view-selected",
15792      /**
15793      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15794      */
15795     emptyText : "",
15796     
15797     /**
15798      * @cfg {String} text to display on mask (default Loading)
15799      */
15800     mask : false,
15801     /**
15802      * @cfg {Boolean} multiSelect Allow multiple selection
15803      */
15804     multiSelect : false,
15805     /**
15806      * @cfg {Boolean} singleSelect Allow single selection
15807      */
15808     singleSelect:  false,
15809     
15810     /**
15811      * @cfg {Boolean} toggleSelect - selecting 
15812      */
15813     toggleSelect : false,
15814     
15815     /**
15816      * @cfg {Boolean} tickable - selecting 
15817      */
15818     tickable : false,
15819     
15820     /**
15821      * Returns the element this view is bound to.
15822      * @return {Roo.Element}
15823      */
15824     getEl : function(){
15825         return this.wrapEl;
15826     },
15827     
15828     
15829
15830     /**
15831      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15832      */
15833     refresh : function(){
15834         //Roo.log('refresh');
15835         var t = this.tpl;
15836         
15837         // if we are using something like 'domtemplate', then
15838         // the what gets used is:
15839         // t.applySubtemplate(NAME, data, wrapping data..)
15840         // the outer template then get' applied with
15841         //     the store 'extra data'
15842         // and the body get's added to the
15843         //      roo-name="data" node?
15844         //      <span class='roo-tpl-{name}'></span> ?????
15845         
15846         
15847         
15848         this.clearSelections();
15849         this.el.update("");
15850         var html = [];
15851         var records = this.store.getRange();
15852         if(records.length < 1) {
15853             
15854             // is this valid??  = should it render a template??
15855             
15856             this.el.update(this.emptyText);
15857             return;
15858         }
15859         var el = this.el;
15860         if (this.dataName) {
15861             this.el.update(t.apply(this.store.meta)); //????
15862             el = this.el.child('.roo-tpl-' + this.dataName);
15863         }
15864         
15865         for(var i = 0, len = records.length; i < len; i++){
15866             var data = this.prepareData(records[i].data, i, records[i]);
15867             this.fireEvent("preparedata", this, data, i, records[i]);
15868             
15869             var d = Roo.apply({}, data);
15870             
15871             if(this.tickable){
15872                 Roo.apply(d, {'roo-id' : Roo.id()});
15873                 
15874                 var _this = this;
15875             
15876                 Roo.each(this.parent.item, function(item){
15877                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15878                         return;
15879                     }
15880                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15881                 });
15882             }
15883             
15884             html[html.length] = Roo.util.Format.trim(
15885                 this.dataName ?
15886                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15887                     t.apply(d)
15888             );
15889         }
15890         
15891         
15892         
15893         el.update(html.join(""));
15894         this.nodes = el.dom.childNodes;
15895         this.updateIndexes(0);
15896     },
15897     
15898
15899     /**
15900      * Function to override to reformat the data that is sent to
15901      * the template for each node.
15902      * DEPRICATED - use the preparedata event handler.
15903      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15904      * a JSON object for an UpdateManager bound view).
15905      */
15906     prepareData : function(data, index, record)
15907     {
15908         this.fireEvent("preparedata", this, data, index, record);
15909         return data;
15910     },
15911
15912     onUpdate : function(ds, record){
15913         // Roo.log('on update');   
15914         this.clearSelections();
15915         var index = this.store.indexOf(record);
15916         var n = this.nodes[index];
15917         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15918         n.parentNode.removeChild(n);
15919         this.updateIndexes(index, index);
15920     },
15921
15922     
15923     
15924 // --------- FIXME     
15925     onAdd : function(ds, records, index)
15926     {
15927         //Roo.log(['on Add', ds, records, index] );        
15928         this.clearSelections();
15929         if(this.nodes.length == 0){
15930             this.refresh();
15931             return;
15932         }
15933         var n = this.nodes[index];
15934         for(var i = 0, len = records.length; i < len; i++){
15935             var d = this.prepareData(records[i].data, i, records[i]);
15936             if(n){
15937                 this.tpl.insertBefore(n, d);
15938             }else{
15939                 
15940                 this.tpl.append(this.el, d);
15941             }
15942         }
15943         this.updateIndexes(index);
15944     },
15945
15946     onRemove : function(ds, record, index){
15947        // Roo.log('onRemove');
15948         this.clearSelections();
15949         var el = this.dataName  ?
15950             this.el.child('.roo-tpl-' + this.dataName) :
15951             this.el; 
15952         
15953         el.dom.removeChild(this.nodes[index]);
15954         this.updateIndexes(index);
15955     },
15956
15957     /**
15958      * Refresh an individual node.
15959      * @param {Number} index
15960      */
15961     refreshNode : function(index){
15962         this.onUpdate(this.store, this.store.getAt(index));
15963     },
15964
15965     updateIndexes : function(startIndex, endIndex){
15966         var ns = this.nodes;
15967         startIndex = startIndex || 0;
15968         endIndex = endIndex || ns.length - 1;
15969         for(var i = startIndex; i <= endIndex; i++){
15970             ns[i].nodeIndex = i;
15971         }
15972     },
15973
15974     /**
15975      * Changes the data store this view uses and refresh the view.
15976      * @param {Store} store
15977      */
15978     setStore : function(store, initial){
15979         if(!initial && this.store){
15980             this.store.un("datachanged", this.refresh);
15981             this.store.un("add", this.onAdd);
15982             this.store.un("remove", this.onRemove);
15983             this.store.un("update", this.onUpdate);
15984             this.store.un("clear", this.refresh);
15985             this.store.un("beforeload", this.onBeforeLoad);
15986             this.store.un("load", this.onLoad);
15987             this.store.un("loadexception", this.onLoad);
15988         }
15989         if(store){
15990           
15991             store.on("datachanged", this.refresh, this);
15992             store.on("add", this.onAdd, this);
15993             store.on("remove", this.onRemove, this);
15994             store.on("update", this.onUpdate, this);
15995             store.on("clear", this.refresh, this);
15996             store.on("beforeload", this.onBeforeLoad, this);
15997             store.on("load", this.onLoad, this);
15998             store.on("loadexception", this.onLoad, this);
15999         }
16000         
16001         if(store){
16002             this.refresh();
16003         }
16004     },
16005     /**
16006      * onbeforeLoad - masks the loading area.
16007      *
16008      */
16009     onBeforeLoad : function(store,opts)
16010     {
16011          //Roo.log('onBeforeLoad');   
16012         if (!opts.add) {
16013             this.el.update("");
16014         }
16015         this.el.mask(this.mask ? this.mask : "Loading" ); 
16016     },
16017     onLoad : function ()
16018     {
16019         this.el.unmask();
16020     },
16021     
16022
16023     /**
16024      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16025      * @param {HTMLElement} node
16026      * @return {HTMLElement} The template node
16027      */
16028     findItemFromChild : function(node){
16029         var el = this.dataName  ?
16030             this.el.child('.roo-tpl-' + this.dataName,true) :
16031             this.el.dom; 
16032         
16033         if(!node || node.parentNode == el){
16034                     return node;
16035             }
16036             var p = node.parentNode;
16037             while(p && p != el){
16038             if(p.parentNode == el){
16039                 return p;
16040             }
16041             p = p.parentNode;
16042         }
16043             return null;
16044     },
16045
16046     /** @ignore */
16047     onClick : function(e){
16048         var item = this.findItemFromChild(e.getTarget());
16049         if(item){
16050             var index = this.indexOf(item);
16051             if(this.onItemClick(item, index, e) !== false){
16052                 this.fireEvent("click", this, index, item, e);
16053             }
16054         }else{
16055             this.clearSelections();
16056         }
16057     },
16058
16059     /** @ignore */
16060     onContextMenu : function(e){
16061         var item = this.findItemFromChild(e.getTarget());
16062         if(item){
16063             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16064         }
16065     },
16066
16067     /** @ignore */
16068     onDblClick : function(e){
16069         var item = this.findItemFromChild(e.getTarget());
16070         if(item){
16071             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16072         }
16073     },
16074
16075     onItemClick : function(item, index, e)
16076     {
16077         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16078             return false;
16079         }
16080         if (this.toggleSelect) {
16081             var m = this.isSelected(item) ? 'unselect' : 'select';
16082             //Roo.log(m);
16083             var _t = this;
16084             _t[m](item, true, false);
16085             return true;
16086         }
16087         if(this.multiSelect || this.singleSelect){
16088             if(this.multiSelect && e.shiftKey && this.lastSelection){
16089                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16090             }else{
16091                 this.select(item, this.multiSelect && e.ctrlKey);
16092                 this.lastSelection = item;
16093             }
16094             
16095             if(!this.tickable){
16096                 e.preventDefault();
16097             }
16098             
16099         }
16100         return true;
16101     },
16102
16103     /**
16104      * Get the number of selected nodes.
16105      * @return {Number}
16106      */
16107     getSelectionCount : function(){
16108         return this.selections.length;
16109     },
16110
16111     /**
16112      * Get the currently selected nodes.
16113      * @return {Array} An array of HTMLElements
16114      */
16115     getSelectedNodes : function(){
16116         return this.selections;
16117     },
16118
16119     /**
16120      * Get the indexes of the selected nodes.
16121      * @return {Array}
16122      */
16123     getSelectedIndexes : function(){
16124         var indexes = [], s = this.selections;
16125         for(var i = 0, len = s.length; i < len; i++){
16126             indexes.push(s[i].nodeIndex);
16127         }
16128         return indexes;
16129     },
16130
16131     /**
16132      * Clear all selections
16133      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16134      */
16135     clearSelections : function(suppressEvent){
16136         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16137             this.cmp.elements = this.selections;
16138             this.cmp.removeClass(this.selectedClass);
16139             this.selections = [];
16140             if(!suppressEvent){
16141                 this.fireEvent("selectionchange", this, this.selections);
16142             }
16143         }
16144     },
16145
16146     /**
16147      * Returns true if the passed node is selected
16148      * @param {HTMLElement/Number} node The node or node index
16149      * @return {Boolean}
16150      */
16151     isSelected : function(node){
16152         var s = this.selections;
16153         if(s.length < 1){
16154             return false;
16155         }
16156         node = this.getNode(node);
16157         return s.indexOf(node) !== -1;
16158     },
16159
16160     /**
16161      * Selects nodes.
16162      * @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
16163      * @param {Boolean} keepExisting (optional) true to keep existing selections
16164      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16165      */
16166     select : function(nodeInfo, keepExisting, suppressEvent){
16167         if(nodeInfo instanceof Array){
16168             if(!keepExisting){
16169                 this.clearSelections(true);
16170             }
16171             for(var i = 0, len = nodeInfo.length; i < len; i++){
16172                 this.select(nodeInfo[i], true, true);
16173             }
16174             return;
16175         } 
16176         var node = this.getNode(nodeInfo);
16177         if(!node || this.isSelected(node)){
16178             return; // already selected.
16179         }
16180         if(!keepExisting){
16181             this.clearSelections(true);
16182         }
16183         
16184         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16185             Roo.fly(node).addClass(this.selectedClass);
16186             this.selections.push(node);
16187             if(!suppressEvent){
16188                 this.fireEvent("selectionchange", this, this.selections);
16189             }
16190         }
16191         
16192         
16193     },
16194       /**
16195      * Unselects nodes.
16196      * @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
16197      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16198      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16199      */
16200     unselect : function(nodeInfo, keepExisting, suppressEvent)
16201     {
16202         if(nodeInfo instanceof Array){
16203             Roo.each(this.selections, function(s) {
16204                 this.unselect(s, nodeInfo);
16205             }, this);
16206             return;
16207         }
16208         var node = this.getNode(nodeInfo);
16209         if(!node || !this.isSelected(node)){
16210             //Roo.log("not selected");
16211             return; // not selected.
16212         }
16213         // fireevent???
16214         var ns = [];
16215         Roo.each(this.selections, function(s) {
16216             if (s == node ) {
16217                 Roo.fly(node).removeClass(this.selectedClass);
16218
16219                 return;
16220             }
16221             ns.push(s);
16222         },this);
16223         
16224         this.selections= ns;
16225         this.fireEvent("selectionchange", this, this.selections);
16226     },
16227
16228     /**
16229      * Gets a template node.
16230      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16231      * @return {HTMLElement} The node or null if it wasn't found
16232      */
16233     getNode : function(nodeInfo){
16234         if(typeof nodeInfo == "string"){
16235             return document.getElementById(nodeInfo);
16236         }else if(typeof nodeInfo == "number"){
16237             return this.nodes[nodeInfo];
16238         }
16239         return nodeInfo;
16240     },
16241
16242     /**
16243      * Gets a range template nodes.
16244      * @param {Number} startIndex
16245      * @param {Number} endIndex
16246      * @return {Array} An array of nodes
16247      */
16248     getNodes : function(start, end){
16249         var ns = this.nodes;
16250         start = start || 0;
16251         end = typeof end == "undefined" ? ns.length - 1 : end;
16252         var nodes = [];
16253         if(start <= end){
16254             for(var i = start; i <= end; i++){
16255                 nodes.push(ns[i]);
16256             }
16257         } else{
16258             for(var i = start; i >= end; i--){
16259                 nodes.push(ns[i]);
16260             }
16261         }
16262         return nodes;
16263     },
16264
16265     /**
16266      * Finds the index of the passed node
16267      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16268      * @return {Number} The index of the node or -1
16269      */
16270     indexOf : function(node){
16271         node = this.getNode(node);
16272         if(typeof node.nodeIndex == "number"){
16273             return node.nodeIndex;
16274         }
16275         var ns = this.nodes;
16276         for(var i = 0, len = ns.length; i < len; i++){
16277             if(ns[i] == node){
16278                 return i;
16279             }
16280         }
16281         return -1;
16282     }
16283 });
16284 /*
16285  * - LGPL
16286  *
16287  * based on jquery fullcalendar
16288  * 
16289  */
16290
16291 Roo.bootstrap = Roo.bootstrap || {};
16292 /**
16293  * @class Roo.bootstrap.Calendar
16294  * @extends Roo.bootstrap.Component
16295  * Bootstrap Calendar class
16296  * @cfg {Boolean} loadMask (true|false) default false
16297  * @cfg {Object} header generate the user specific header of the calendar, default false
16298
16299  * @constructor
16300  * Create a new Container
16301  * @param {Object} config The config object
16302  */
16303
16304
16305
16306 Roo.bootstrap.Calendar = function(config){
16307     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16308      this.addEvents({
16309         /**
16310              * @event select
16311              * Fires when a date is selected
16312              * @param {DatePicker} this
16313              * @param {Date} date The selected date
16314              */
16315         'select': true,
16316         /**
16317              * @event monthchange
16318              * Fires when the displayed month changes 
16319              * @param {DatePicker} this
16320              * @param {Date} date The selected month
16321              */
16322         'monthchange': true,
16323         /**
16324              * @event evententer
16325              * Fires when mouse over an event
16326              * @param {Calendar} this
16327              * @param {event} Event
16328              */
16329         'evententer': true,
16330         /**
16331              * @event eventleave
16332              * Fires when the mouse leaves an
16333              * @param {Calendar} this
16334              * @param {event}
16335              */
16336         'eventleave': true,
16337         /**
16338              * @event eventclick
16339              * Fires when the mouse click an
16340              * @param {Calendar} this
16341              * @param {event}
16342              */
16343         'eventclick': true
16344         
16345     });
16346
16347 };
16348
16349 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16350     
16351      /**
16352      * @cfg {Number} startDay
16353      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16354      */
16355     startDay : 0,
16356     
16357     loadMask : false,
16358     
16359     header : false,
16360       
16361     getAutoCreate : function(){
16362         
16363         
16364         var fc_button = function(name, corner, style, content ) {
16365             return Roo.apply({},{
16366                 tag : 'span',
16367                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16368                          (corner.length ?
16369                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16370                             ''
16371                         ),
16372                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16373                 unselectable: 'on'
16374             });
16375         };
16376         
16377         var header = {};
16378         
16379         if(!this.header){
16380             header = {
16381                 tag : 'table',
16382                 cls : 'fc-header',
16383                 style : 'width:100%',
16384                 cn : [
16385                     {
16386                         tag: 'tr',
16387                         cn : [
16388                             {
16389                                 tag : 'td',
16390                                 cls : 'fc-header-left',
16391                                 cn : [
16392                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16393                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16394                                     { tag: 'span', cls: 'fc-header-space' },
16395                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16396
16397
16398                                 ]
16399                             },
16400
16401                             {
16402                                 tag : 'td',
16403                                 cls : 'fc-header-center',
16404                                 cn : [
16405                                     {
16406                                         tag: 'span',
16407                                         cls: 'fc-header-title',
16408                                         cn : {
16409                                             tag: 'H2',
16410                                             html : 'month / year'
16411                                         }
16412                                     }
16413
16414                                 ]
16415                             },
16416                             {
16417                                 tag : 'td',
16418                                 cls : 'fc-header-right',
16419                                 cn : [
16420                               /*      fc_button('month', 'left', '', 'month' ),
16421                                     fc_button('week', '', '', 'week' ),
16422                                     fc_button('day', 'right', '', 'day' )
16423                                 */    
16424
16425                                 ]
16426                             }
16427
16428                         ]
16429                     }
16430                 ]
16431             };
16432         }
16433         
16434         header = this.header;
16435         
16436        
16437         var cal_heads = function() {
16438             var ret = [];
16439             // fixme - handle this.
16440             
16441             for (var i =0; i < Date.dayNames.length; i++) {
16442                 var d = Date.dayNames[i];
16443                 ret.push({
16444                     tag: 'th',
16445                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16446                     html : d.substring(0,3)
16447                 });
16448                 
16449             }
16450             ret[0].cls += ' fc-first';
16451             ret[6].cls += ' fc-last';
16452             return ret;
16453         };
16454         var cal_cell = function(n) {
16455             return  {
16456                 tag: 'td',
16457                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16458                 cn : [
16459                     {
16460                         cn : [
16461                             {
16462                                 cls: 'fc-day-number',
16463                                 html: 'D'
16464                             },
16465                             {
16466                                 cls: 'fc-day-content',
16467                              
16468                                 cn : [
16469                                      {
16470                                         style: 'position: relative;' // height: 17px;
16471                                     }
16472                                 ]
16473                             }
16474                             
16475                             
16476                         ]
16477                     }
16478                 ]
16479                 
16480             }
16481         };
16482         var cal_rows = function() {
16483             
16484             var ret = [];
16485             for (var r = 0; r < 6; r++) {
16486                 var row= {
16487                     tag : 'tr',
16488                     cls : 'fc-week',
16489                     cn : []
16490                 };
16491                 
16492                 for (var i =0; i < Date.dayNames.length; i++) {
16493                     var d = Date.dayNames[i];
16494                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16495
16496                 }
16497                 row.cn[0].cls+=' fc-first';
16498                 row.cn[0].cn[0].style = 'min-height:90px';
16499                 row.cn[6].cls+=' fc-last';
16500                 ret.push(row);
16501                 
16502             }
16503             ret[0].cls += ' fc-first';
16504             ret[4].cls += ' fc-prev-last';
16505             ret[5].cls += ' fc-last';
16506             return ret;
16507             
16508         };
16509         
16510         var cal_table = {
16511             tag: 'table',
16512             cls: 'fc-border-separate',
16513             style : 'width:100%',
16514             cellspacing  : 0,
16515             cn : [
16516                 { 
16517                     tag: 'thead',
16518                     cn : [
16519                         { 
16520                             tag: 'tr',
16521                             cls : 'fc-first fc-last',
16522                             cn : cal_heads()
16523                         }
16524                     ]
16525                 },
16526                 { 
16527                     tag: 'tbody',
16528                     cn : cal_rows()
16529                 }
16530                   
16531             ]
16532         };
16533          
16534          var cfg = {
16535             cls : 'fc fc-ltr',
16536             cn : [
16537                 header,
16538                 {
16539                     cls : 'fc-content',
16540                     style : "position: relative;",
16541                     cn : [
16542                         {
16543                             cls : 'fc-view fc-view-month fc-grid',
16544                             style : 'position: relative',
16545                             unselectable : 'on',
16546                             cn : [
16547                                 {
16548                                     cls : 'fc-event-container',
16549                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16550                                 },
16551                                 cal_table
16552                             ]
16553                         }
16554                     ]
16555     
16556                 }
16557            ] 
16558             
16559         };
16560         
16561          
16562         
16563         return cfg;
16564     },
16565     
16566     
16567     initEvents : function()
16568     {
16569         if(!this.store){
16570             throw "can not find store for calendar";
16571         }
16572         
16573         var mark = {
16574             tag: "div",
16575             cls:"x-dlg-mask",
16576             style: "text-align:center",
16577             cn: [
16578                 {
16579                     tag: "div",
16580                     style: "background-color:white;width:50%;margin:250 auto",
16581                     cn: [
16582                         {
16583                             tag: "img",
16584                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16585                         },
16586                         {
16587                             tag: "span",
16588                             html: "Loading"
16589                         }
16590                         
16591                     ]
16592                 }
16593             ]
16594         };
16595         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16596         
16597         var size = this.el.select('.fc-content', true).first().getSize();
16598         this.maskEl.setSize(size.width, size.height);
16599         this.maskEl.enableDisplayMode("block");
16600         if(!this.loadMask){
16601             this.maskEl.hide();
16602         }
16603         
16604         this.store = Roo.factory(this.store, Roo.data);
16605         this.store.on('load', this.onLoad, this);
16606         this.store.on('beforeload', this.onBeforeLoad, this);
16607         
16608         this.resize();
16609         
16610         this.cells = this.el.select('.fc-day',true);
16611         //Roo.log(this.cells);
16612         this.textNodes = this.el.query('.fc-day-number');
16613         this.cells.addClassOnOver('fc-state-hover');
16614         
16615         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16616         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16617         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16618         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16619         
16620         this.on('monthchange', this.onMonthChange, this);
16621         
16622         this.update(new Date().clearTime());
16623     },
16624     
16625     resize : function() {
16626         var sz  = this.el.getSize();
16627         
16628         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16629         this.el.select('.fc-day-content div',true).setHeight(34);
16630     },
16631     
16632     
16633     // private
16634     showPrevMonth : function(e){
16635         this.update(this.activeDate.add("mo", -1));
16636     },
16637     showToday : function(e){
16638         this.update(new Date().clearTime());
16639     },
16640     // private
16641     showNextMonth : function(e){
16642         this.update(this.activeDate.add("mo", 1));
16643     },
16644
16645     // private
16646     showPrevYear : function(){
16647         this.update(this.activeDate.add("y", -1));
16648     },
16649
16650     // private
16651     showNextYear : function(){
16652         this.update(this.activeDate.add("y", 1));
16653     },
16654
16655     
16656    // private
16657     update : function(date)
16658     {
16659         var vd = this.activeDate;
16660         this.activeDate = date;
16661 //        if(vd && this.el){
16662 //            var t = date.getTime();
16663 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16664 //                Roo.log('using add remove');
16665 //                
16666 //                this.fireEvent('monthchange', this, date);
16667 //                
16668 //                this.cells.removeClass("fc-state-highlight");
16669 //                this.cells.each(function(c){
16670 //                   if(c.dateValue == t){
16671 //                       c.addClass("fc-state-highlight");
16672 //                       setTimeout(function(){
16673 //                            try{c.dom.firstChild.focus();}catch(e){}
16674 //                       }, 50);
16675 //                       return false;
16676 //                   }
16677 //                   return true;
16678 //                });
16679 //                return;
16680 //            }
16681 //        }
16682         
16683         var days = date.getDaysInMonth();
16684         
16685         var firstOfMonth = date.getFirstDateOfMonth();
16686         var startingPos = firstOfMonth.getDay()-this.startDay;
16687         
16688         if(startingPos < this.startDay){
16689             startingPos += 7;
16690         }
16691         
16692         var pm = date.add(Date.MONTH, -1);
16693         var prevStart = pm.getDaysInMonth()-startingPos;
16694 //        
16695         this.cells = this.el.select('.fc-day',true);
16696         this.textNodes = this.el.query('.fc-day-number');
16697         this.cells.addClassOnOver('fc-state-hover');
16698         
16699         var cells = this.cells.elements;
16700         var textEls = this.textNodes;
16701         
16702         Roo.each(cells, function(cell){
16703             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16704         });
16705         
16706         days += startingPos;
16707
16708         // convert everything to numbers so it's fast
16709         var day = 86400000;
16710         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16711         //Roo.log(d);
16712         //Roo.log(pm);
16713         //Roo.log(prevStart);
16714         
16715         var today = new Date().clearTime().getTime();
16716         var sel = date.clearTime().getTime();
16717         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16718         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16719         var ddMatch = this.disabledDatesRE;
16720         var ddText = this.disabledDatesText;
16721         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16722         var ddaysText = this.disabledDaysText;
16723         var format = this.format;
16724         
16725         var setCellClass = function(cal, cell){
16726             cell.row = 0;
16727             cell.events = [];
16728             cell.more = [];
16729             //Roo.log('set Cell Class');
16730             cell.title = "";
16731             var t = d.getTime();
16732             
16733             //Roo.log(d);
16734             
16735             cell.dateValue = t;
16736             if(t == today){
16737                 cell.className += " fc-today";
16738                 cell.className += " fc-state-highlight";
16739                 cell.title = cal.todayText;
16740             }
16741             if(t == sel){
16742                 // disable highlight in other month..
16743                 //cell.className += " fc-state-highlight";
16744                 
16745             }
16746             // disabling
16747             if(t < min) {
16748                 cell.className = " fc-state-disabled";
16749                 cell.title = cal.minText;
16750                 return;
16751             }
16752             if(t > max) {
16753                 cell.className = " fc-state-disabled";
16754                 cell.title = cal.maxText;
16755                 return;
16756             }
16757             if(ddays){
16758                 if(ddays.indexOf(d.getDay()) != -1){
16759                     cell.title = ddaysText;
16760                     cell.className = " fc-state-disabled";
16761                 }
16762             }
16763             if(ddMatch && format){
16764                 var fvalue = d.dateFormat(format);
16765                 if(ddMatch.test(fvalue)){
16766                     cell.title = ddText.replace("%0", fvalue);
16767                     cell.className = " fc-state-disabled";
16768                 }
16769             }
16770             
16771             if (!cell.initialClassName) {
16772                 cell.initialClassName = cell.dom.className;
16773             }
16774             
16775             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16776         };
16777
16778         var i = 0;
16779         
16780         for(; i < startingPos; i++) {
16781             textEls[i].innerHTML = (++prevStart);
16782             d.setDate(d.getDate()+1);
16783             
16784             cells[i].className = "fc-past fc-other-month";
16785             setCellClass(this, cells[i]);
16786         }
16787         
16788         var intDay = 0;
16789         
16790         for(; i < days; i++){
16791             intDay = i - startingPos + 1;
16792             textEls[i].innerHTML = (intDay);
16793             d.setDate(d.getDate()+1);
16794             
16795             cells[i].className = ''; // "x-date-active";
16796             setCellClass(this, cells[i]);
16797         }
16798         var extraDays = 0;
16799         
16800         for(; i < 42; i++) {
16801             textEls[i].innerHTML = (++extraDays);
16802             d.setDate(d.getDate()+1);
16803             
16804             cells[i].className = "fc-future fc-other-month";
16805             setCellClass(this, cells[i]);
16806         }
16807         
16808         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16809         
16810         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16811         
16812         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16813         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16814         
16815         if(totalRows != 6){
16816             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16817             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16818         }
16819         
16820         this.fireEvent('monthchange', this, date);
16821         
16822         
16823         /*
16824         if(!this.internalRender){
16825             var main = this.el.dom.firstChild;
16826             var w = main.offsetWidth;
16827             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16828             Roo.fly(main).setWidth(w);
16829             this.internalRender = true;
16830             // opera does not respect the auto grow header center column
16831             // then, after it gets a width opera refuses to recalculate
16832             // without a second pass
16833             if(Roo.isOpera && !this.secondPass){
16834                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16835                 this.secondPass = true;
16836                 this.update.defer(10, this, [date]);
16837             }
16838         }
16839         */
16840         
16841     },
16842     
16843     findCell : function(dt) {
16844         dt = dt.clearTime().getTime();
16845         var ret = false;
16846         this.cells.each(function(c){
16847             //Roo.log("check " +c.dateValue + '?=' + dt);
16848             if(c.dateValue == dt){
16849                 ret = c;
16850                 return false;
16851             }
16852             return true;
16853         });
16854         
16855         return ret;
16856     },
16857     
16858     findCells : function(ev) {
16859         var s = ev.start.clone().clearTime().getTime();
16860        // Roo.log(s);
16861         var e= ev.end.clone().clearTime().getTime();
16862        // Roo.log(e);
16863         var ret = [];
16864         this.cells.each(function(c){
16865              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16866             
16867             if(c.dateValue > e){
16868                 return ;
16869             }
16870             if(c.dateValue < s){
16871                 return ;
16872             }
16873             ret.push(c);
16874         });
16875         
16876         return ret;    
16877     },
16878     
16879 //    findBestRow: function(cells)
16880 //    {
16881 //        var ret = 0;
16882 //        
16883 //        for (var i =0 ; i < cells.length;i++) {
16884 //            ret  = Math.max(cells[i].rows || 0,ret);
16885 //        }
16886 //        return ret;
16887 //        
16888 //    },
16889     
16890     
16891     addItem : function(ev)
16892     {
16893         // look for vertical location slot in
16894         var cells = this.findCells(ev);
16895         
16896 //        ev.row = this.findBestRow(cells);
16897         
16898         // work out the location.
16899         
16900         var crow = false;
16901         var rows = [];
16902         for(var i =0; i < cells.length; i++) {
16903             
16904             cells[i].row = cells[0].row;
16905             
16906             if(i == 0){
16907                 cells[i].row = cells[i].row + 1;
16908             }
16909             
16910             if (!crow) {
16911                 crow = {
16912                     start : cells[i],
16913                     end :  cells[i]
16914                 };
16915                 continue;
16916             }
16917             if (crow.start.getY() == cells[i].getY()) {
16918                 // on same row.
16919                 crow.end = cells[i];
16920                 continue;
16921             }
16922             // different row.
16923             rows.push(crow);
16924             crow = {
16925                 start: cells[i],
16926                 end : cells[i]
16927             };
16928             
16929         }
16930         
16931         rows.push(crow);
16932         ev.els = [];
16933         ev.rows = rows;
16934         ev.cells = cells;
16935         
16936         cells[0].events.push(ev);
16937         
16938         this.calevents.push(ev);
16939     },
16940     
16941     clearEvents: function() {
16942         
16943         if(!this.calevents){
16944             return;
16945         }
16946         
16947         Roo.each(this.cells.elements, function(c){
16948             c.row = 0;
16949             c.events = [];
16950             c.more = [];
16951         });
16952         
16953         Roo.each(this.calevents, function(e) {
16954             Roo.each(e.els, function(el) {
16955                 el.un('mouseenter' ,this.onEventEnter, this);
16956                 el.un('mouseleave' ,this.onEventLeave, this);
16957                 el.remove();
16958             },this);
16959         },this);
16960         
16961         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16962             e.remove();
16963         });
16964         
16965     },
16966     
16967     renderEvents: function()
16968     {   
16969         var _this = this;
16970         
16971         this.cells.each(function(c) {
16972             
16973             if(c.row < 5){
16974                 return;
16975             }
16976             
16977             var ev = c.events;
16978             
16979             var r = 4;
16980             if(c.row != c.events.length){
16981                 r = 4 - (4 - (c.row - c.events.length));
16982             }
16983             
16984             c.events = ev.slice(0, r);
16985             c.more = ev.slice(r);
16986             
16987             if(c.more.length && c.more.length == 1){
16988                 c.events.push(c.more.pop());
16989             }
16990             
16991             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16992             
16993         });
16994             
16995         this.cells.each(function(c) {
16996             
16997             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16998             
16999             
17000             for (var e = 0; e < c.events.length; e++){
17001                 var ev = c.events[e];
17002                 var rows = ev.rows;
17003                 
17004                 for(var i = 0; i < rows.length; i++) {
17005                 
17006                     // how many rows should it span..
17007
17008                     var  cfg = {
17009                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17010                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17011
17012                         unselectable : "on",
17013                         cn : [
17014                             {
17015                                 cls: 'fc-event-inner',
17016                                 cn : [
17017     //                                {
17018     //                                  tag:'span',
17019     //                                  cls: 'fc-event-time',
17020     //                                  html : cells.length > 1 ? '' : ev.time
17021     //                                },
17022                                     {
17023                                       tag:'span',
17024                                       cls: 'fc-event-title',
17025                                       html : String.format('{0}', ev.title)
17026                                     }
17027
17028
17029                                 ]
17030                             },
17031                             {
17032                                 cls: 'ui-resizable-handle ui-resizable-e',
17033                                 html : '&nbsp;&nbsp;&nbsp'
17034                             }
17035
17036                         ]
17037                     };
17038
17039                     if (i == 0) {
17040                         cfg.cls += ' fc-event-start';
17041                     }
17042                     if ((i+1) == rows.length) {
17043                         cfg.cls += ' fc-event-end';
17044                     }
17045
17046                     var ctr = _this.el.select('.fc-event-container',true).first();
17047                     var cg = ctr.createChild(cfg);
17048
17049                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17050                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17051
17052                     var r = (c.more.length) ? 1 : 0;
17053                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17054                     cg.setWidth(ebox.right - sbox.x -2);
17055
17056                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17057                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17058                     cg.on('click', _this.onEventClick, _this, ev);
17059
17060                     ev.els.push(cg);
17061                     
17062                 }
17063                 
17064             }
17065             
17066             
17067             if(c.more.length){
17068                 var  cfg = {
17069                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17070                     style : 'position: absolute',
17071                     unselectable : "on",
17072                     cn : [
17073                         {
17074                             cls: 'fc-event-inner',
17075                             cn : [
17076                                 {
17077                                   tag:'span',
17078                                   cls: 'fc-event-title',
17079                                   html : 'More'
17080                                 }
17081
17082
17083                             ]
17084                         },
17085                         {
17086                             cls: 'ui-resizable-handle ui-resizable-e',
17087                             html : '&nbsp;&nbsp;&nbsp'
17088                         }
17089
17090                     ]
17091                 };
17092
17093                 var ctr = _this.el.select('.fc-event-container',true).first();
17094                 var cg = ctr.createChild(cfg);
17095
17096                 var sbox = c.select('.fc-day-content',true).first().getBox();
17097                 var ebox = c.select('.fc-day-content',true).first().getBox();
17098                 //Roo.log(cg);
17099                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17100                 cg.setWidth(ebox.right - sbox.x -2);
17101
17102                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17103                 
17104             }
17105             
17106         });
17107         
17108         
17109         
17110     },
17111     
17112     onEventEnter: function (e, el,event,d) {
17113         this.fireEvent('evententer', this, el, event);
17114     },
17115     
17116     onEventLeave: function (e, el,event,d) {
17117         this.fireEvent('eventleave', this, el, event);
17118     },
17119     
17120     onEventClick: function (e, el,event,d) {
17121         this.fireEvent('eventclick', this, el, event);
17122     },
17123     
17124     onMonthChange: function () {
17125         this.store.load();
17126     },
17127     
17128     onMoreEventClick: function(e, el, more)
17129     {
17130         var _this = this;
17131         
17132         this.calpopover.placement = 'right';
17133         this.calpopover.setTitle('More');
17134         
17135         this.calpopover.setContent('');
17136         
17137         var ctr = this.calpopover.el.select('.popover-content', true).first();
17138         
17139         Roo.each(more, function(m){
17140             var cfg = {
17141                 cls : 'fc-event-hori fc-event-draggable',
17142                 html : m.title
17143             };
17144             var cg = ctr.createChild(cfg);
17145             
17146             cg.on('click', _this.onEventClick, _this, m);
17147         });
17148         
17149         this.calpopover.show(el);
17150         
17151         
17152     },
17153     
17154     onLoad: function () 
17155     {   
17156         this.calevents = [];
17157         var cal = this;
17158         
17159         if(this.store.getCount() > 0){
17160             this.store.data.each(function(d){
17161                cal.addItem({
17162                     id : d.data.id,
17163                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17164                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17165                     time : d.data.start_time,
17166                     title : d.data.title,
17167                     description : d.data.description,
17168                     venue : d.data.venue
17169                 });
17170             });
17171         }
17172         
17173         this.renderEvents();
17174         
17175         if(this.calevents.length && this.loadMask){
17176             this.maskEl.hide();
17177         }
17178     },
17179     
17180     onBeforeLoad: function()
17181     {
17182         this.clearEvents();
17183         if(this.loadMask){
17184             this.maskEl.show();
17185         }
17186     }
17187 });
17188
17189  
17190  /*
17191  * - LGPL
17192  *
17193  * element
17194  * 
17195  */
17196
17197 /**
17198  * @class Roo.bootstrap.Popover
17199  * @extends Roo.bootstrap.Component
17200  * Bootstrap Popover class
17201  * @cfg {String} html contents of the popover   (or false to use children..)
17202  * @cfg {String} title of popover (or false to hide)
17203  * @cfg {String} placement how it is placed
17204  * @cfg {String} trigger click || hover (or false to trigger manually)
17205  * @cfg {String} over what (parent or false to trigger manually.)
17206  * @cfg {Number} delay - delay before showing
17207  
17208  * @constructor
17209  * Create a new Popover
17210  * @param {Object} config The config object
17211  */
17212
17213 Roo.bootstrap.Popover = function(config){
17214     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17215     
17216     this.addEvents({
17217         // raw events
17218          /**
17219          * @event show
17220          * After the popover show
17221          * 
17222          * @param {Roo.bootstrap.Popover} this
17223          */
17224         "show" : true,
17225         /**
17226          * @event hide
17227          * After the popover hide
17228          * 
17229          * @param {Roo.bootstrap.Popover} this
17230          */
17231         "hide" : true
17232     });
17233 };
17234
17235 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17236     
17237     title: 'Fill in a title',
17238     html: false,
17239     
17240     placement : 'right',
17241     trigger : 'hover', // hover
17242     
17243     delay : 0,
17244     
17245     over: 'parent',
17246     
17247     can_build_overlaid : false,
17248     
17249     getChildContainer : function()
17250     {
17251         return this.el.select('.popover-content',true).first();
17252     },
17253     
17254     getAutoCreate : function(){
17255          
17256         var cfg = {
17257            cls : 'popover roo-dynamic',
17258            style: 'display:block',
17259            cn : [
17260                 {
17261                     cls : 'arrow'
17262                 },
17263                 {
17264                     cls : 'popover-inner',
17265                     cn : [
17266                         {
17267                             tag: 'h3',
17268                             cls: 'popover-title',
17269                             html : this.title
17270                         },
17271                         {
17272                             cls : 'popover-content',
17273                             html : this.html
17274                         }
17275                     ]
17276                     
17277                 }
17278            ]
17279         };
17280         
17281         return cfg;
17282     },
17283     setTitle: function(str)
17284     {
17285         this.title = str;
17286         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17287     },
17288     setContent: function(str)
17289     {
17290         this.html = str;
17291         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17292     },
17293     // as it get's added to the bottom of the page.
17294     onRender : function(ct, position)
17295     {
17296         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17297         if(!this.el){
17298             var cfg = Roo.apply({},  this.getAutoCreate());
17299             cfg.id = Roo.id();
17300             
17301             if (this.cls) {
17302                 cfg.cls += ' ' + this.cls;
17303             }
17304             if (this.style) {
17305                 cfg.style = this.style;
17306             }
17307             //Roo.log("adding to ");
17308             this.el = Roo.get(document.body).createChild(cfg, position);
17309 //            Roo.log(this.el);
17310         }
17311         this.initEvents();
17312     },
17313     
17314     initEvents : function()
17315     {
17316         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17317         this.el.enableDisplayMode('block');
17318         this.el.hide();
17319         if (this.over === false) {
17320             return; 
17321         }
17322         if (this.triggers === false) {
17323             return;
17324         }
17325         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17326         var triggers = this.trigger ? this.trigger.split(' ') : [];
17327         Roo.each(triggers, function(trigger) {
17328         
17329             if (trigger == 'click') {
17330                 on_el.on('click', this.toggle, this);
17331             } else if (trigger != 'manual') {
17332                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17333                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17334       
17335                 on_el.on(eventIn  ,this.enter, this);
17336                 on_el.on(eventOut, this.leave, this);
17337             }
17338         }, this);
17339         
17340     },
17341     
17342     
17343     // private
17344     timeout : null,
17345     hoverState : null,
17346     
17347     toggle : function () {
17348         this.hoverState == 'in' ? this.leave() : this.enter();
17349     },
17350     
17351     enter : function () {
17352         
17353         clearTimeout(this.timeout);
17354     
17355         this.hoverState = 'in';
17356     
17357         if (!this.delay || !this.delay.show) {
17358             this.show();
17359             return;
17360         }
17361         var _t = this;
17362         this.timeout = setTimeout(function () {
17363             if (_t.hoverState == 'in') {
17364                 _t.show();
17365             }
17366         }, this.delay.show)
17367     },
17368     
17369     leave : function() {
17370         clearTimeout(this.timeout);
17371     
17372         this.hoverState = 'out';
17373     
17374         if (!this.delay || !this.delay.hide) {
17375             this.hide();
17376             return;
17377         }
17378         var _t = this;
17379         this.timeout = setTimeout(function () {
17380             if (_t.hoverState == 'out') {
17381                 _t.hide();
17382             }
17383         }, this.delay.hide)
17384     },
17385     
17386     show : function (on_el)
17387     {
17388         if (!on_el) {
17389             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17390         }
17391         
17392         // set content.
17393         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17394         if (this.html !== false) {
17395             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17396         }
17397         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17398         if (!this.title.length) {
17399             this.el.select('.popover-title',true).hide();
17400         }
17401         
17402         var placement = typeof this.placement == 'function' ?
17403             this.placement.call(this, this.el, on_el) :
17404             this.placement;
17405             
17406         var autoToken = /\s?auto?\s?/i;
17407         var autoPlace = autoToken.test(placement);
17408         if (autoPlace) {
17409             placement = placement.replace(autoToken, '') || 'top';
17410         }
17411         
17412         //this.el.detach()
17413         //this.el.setXY([0,0]);
17414         this.el.show();
17415         this.el.dom.style.display='block';
17416         this.el.addClass(placement);
17417         
17418         //this.el.appendTo(on_el);
17419         
17420         var p = this.getPosition();
17421         var box = this.el.getBox();
17422         
17423         if (autoPlace) {
17424             // fixme..
17425         }
17426         var align = Roo.bootstrap.Popover.alignment[placement];
17427         
17428 //        Roo.log(align);
17429         this.el.alignTo(on_el, align[0],align[1]);
17430         //var arrow = this.el.select('.arrow',true).first();
17431         //arrow.set(align[2], 
17432         
17433         this.el.addClass('in');
17434         
17435         
17436         if (this.el.hasClass('fade')) {
17437             // fade it?
17438         }
17439         
17440         this.hoverState = 'in';
17441         
17442         this.fireEvent('show', this);
17443         
17444     },
17445     hide : function()
17446     {
17447         this.el.setXY([0,0]);
17448         this.el.removeClass('in');
17449         this.el.hide();
17450         this.hoverState = null;
17451         
17452         this.fireEvent('hide', this);
17453     }
17454     
17455 });
17456
17457 Roo.bootstrap.Popover.alignment = {
17458     'left' : ['r-l', [-10,0], 'right'],
17459     'right' : ['l-r', [10,0], 'left'],
17460     'bottom' : ['t-b', [0,10], 'top'],
17461     'top' : [ 'b-t', [0,-10], 'bottom']
17462 };
17463
17464  /*
17465  * - LGPL
17466  *
17467  * Progress
17468  * 
17469  */
17470
17471 /**
17472  * @class Roo.bootstrap.Progress
17473  * @extends Roo.bootstrap.Component
17474  * Bootstrap Progress class
17475  * @cfg {Boolean} striped striped of the progress bar
17476  * @cfg {Boolean} active animated of the progress bar
17477  * 
17478  * 
17479  * @constructor
17480  * Create a new Progress
17481  * @param {Object} config The config object
17482  */
17483
17484 Roo.bootstrap.Progress = function(config){
17485     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17486 };
17487
17488 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17489     
17490     striped : false,
17491     active: false,
17492     
17493     getAutoCreate : function(){
17494         var cfg = {
17495             tag: 'div',
17496             cls: 'progress'
17497         };
17498         
17499         
17500         if(this.striped){
17501             cfg.cls += ' progress-striped';
17502         }
17503       
17504         if(this.active){
17505             cfg.cls += ' active';
17506         }
17507         
17508         
17509         return cfg;
17510     }
17511    
17512 });
17513
17514  
17515
17516  /*
17517  * - LGPL
17518  *
17519  * ProgressBar
17520  * 
17521  */
17522
17523 /**
17524  * @class Roo.bootstrap.ProgressBar
17525  * @extends Roo.bootstrap.Component
17526  * Bootstrap ProgressBar class
17527  * @cfg {Number} aria_valuenow aria-value now
17528  * @cfg {Number} aria_valuemin aria-value min
17529  * @cfg {Number} aria_valuemax aria-value max
17530  * @cfg {String} label label for the progress bar
17531  * @cfg {String} panel (success | info | warning | danger )
17532  * @cfg {String} role role of the progress bar
17533  * @cfg {String} sr_only text
17534  * 
17535  * 
17536  * @constructor
17537  * Create a new ProgressBar
17538  * @param {Object} config The config object
17539  */
17540
17541 Roo.bootstrap.ProgressBar = function(config){
17542     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17543 };
17544
17545 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17546     
17547     aria_valuenow : 0,
17548     aria_valuemin : 0,
17549     aria_valuemax : 100,
17550     label : false,
17551     panel : false,
17552     role : false,
17553     sr_only: false,
17554     
17555     getAutoCreate : function()
17556     {
17557         
17558         var cfg = {
17559             tag: 'div',
17560             cls: 'progress-bar',
17561             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17562         };
17563         
17564         if(this.sr_only){
17565             cfg.cn = {
17566                 tag: 'span',
17567                 cls: 'sr-only',
17568                 html: this.sr_only
17569             }
17570         }
17571         
17572         if(this.role){
17573             cfg.role = this.role;
17574         }
17575         
17576         if(this.aria_valuenow){
17577             cfg['aria-valuenow'] = this.aria_valuenow;
17578         }
17579         
17580         if(this.aria_valuemin){
17581             cfg['aria-valuemin'] = this.aria_valuemin;
17582         }
17583         
17584         if(this.aria_valuemax){
17585             cfg['aria-valuemax'] = this.aria_valuemax;
17586         }
17587         
17588         if(this.label && !this.sr_only){
17589             cfg.html = this.label;
17590         }
17591         
17592         if(this.panel){
17593             cfg.cls += ' progress-bar-' + this.panel;
17594         }
17595         
17596         return cfg;
17597     },
17598     
17599     update : function(aria_valuenow)
17600     {
17601         this.aria_valuenow = aria_valuenow;
17602         
17603         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17604     }
17605    
17606 });
17607
17608  
17609
17610  /*
17611  * - LGPL
17612  *
17613  * column
17614  * 
17615  */
17616
17617 /**
17618  * @class Roo.bootstrap.TabGroup
17619  * @extends Roo.bootstrap.Column
17620  * Bootstrap Column class
17621  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17622  * @cfg {Boolean} carousel true to make the group behave like a carousel
17623  * @cfg {Boolean} bullets show bullets for the panels
17624  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17625  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17626  * @cfg {Boolean} showarrow (true|false) show arrow default true
17627  * 
17628  * @constructor
17629  * Create a new TabGroup
17630  * @param {Object} config The config object
17631  */
17632
17633 Roo.bootstrap.TabGroup = function(config){
17634     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17635     if (!this.navId) {
17636         this.navId = Roo.id();
17637     }
17638     this.tabs = [];
17639     Roo.bootstrap.TabGroup.register(this);
17640     
17641 };
17642
17643 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17644     
17645     carousel : false,
17646     transition : false,
17647     bullets : 0,
17648     timer : 0,
17649     autoslide : false,
17650     slideFn : false,
17651     slideOnTouch : false,
17652     showarrow : true,
17653     
17654     getAutoCreate : function()
17655     {
17656         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17657         
17658         cfg.cls += ' tab-content';
17659         
17660         if (this.carousel) {
17661             cfg.cls += ' carousel slide';
17662             
17663             cfg.cn = [{
17664                cls : 'carousel-inner',
17665                cn : []
17666             }];
17667         
17668             if(this.bullets  && !Roo.isTouch){
17669                 
17670                 var bullets = {
17671                     cls : 'carousel-bullets',
17672                     cn : []
17673                 };
17674                
17675                 if(this.bullets_cls){
17676                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17677                 }
17678                 
17679                 bullets.cn.push({
17680                     cls : 'clear'
17681                 });
17682                 
17683                 cfg.cn[0].cn.push(bullets);
17684             }
17685             
17686             if(this.showarrow){
17687                 cfg.cn[0].cn.push({
17688                     tag : 'div',
17689                     class : 'carousel-arrow',
17690                     cn : [
17691                         {
17692                             tag : 'div',
17693                             class : 'carousel-prev',
17694                             cn : [
17695                                 {
17696                                     tag : 'i',
17697                                     class : 'fa fa-chevron-left'
17698                                 }
17699                             ]
17700                         },
17701                         {
17702                             tag : 'div',
17703                             class : 'carousel-next',
17704                             cn : [
17705                                 {
17706                                     tag : 'i',
17707                                     class : 'fa fa-chevron-right'
17708                                 }
17709                             ]
17710                         }
17711                     ]
17712                 });
17713             }
17714             
17715         }
17716         
17717         return cfg;
17718     },
17719     
17720     initEvents:  function()
17721     {
17722 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17723 //            this.el.on("touchstart", this.onTouchStart, this);
17724 //        }
17725         
17726         if(this.autoslide){
17727             var _this = this;
17728             
17729             this.slideFn = window.setInterval(function() {
17730                 _this.showPanelNext();
17731             }, this.timer);
17732         }
17733         
17734         if(this.showarrow){
17735             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17736             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17737         }
17738         
17739         
17740     },
17741     
17742 //    onTouchStart : function(e, el, o)
17743 //    {
17744 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17745 //            return;
17746 //        }
17747 //        
17748 //        this.showPanelNext();
17749 //    },
17750     
17751     
17752     getChildContainer : function()
17753     {
17754         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17755     },
17756     
17757     /**
17758     * register a Navigation item
17759     * @param {Roo.bootstrap.NavItem} the navitem to add
17760     */
17761     register : function(item)
17762     {
17763         this.tabs.push( item);
17764         item.navId = this.navId; // not really needed..
17765         this.addBullet();
17766     
17767     },
17768     
17769     getActivePanel : function()
17770     {
17771         var r = false;
17772         Roo.each(this.tabs, function(t) {
17773             if (t.active) {
17774                 r = t;
17775                 return false;
17776             }
17777             return null;
17778         });
17779         return r;
17780         
17781     },
17782     getPanelByName : function(n)
17783     {
17784         var r = false;
17785         Roo.each(this.tabs, function(t) {
17786             if (t.tabId == n) {
17787                 r = t;
17788                 return false;
17789             }
17790             return null;
17791         });
17792         return r;
17793     },
17794     indexOfPanel : function(p)
17795     {
17796         var r = false;
17797         Roo.each(this.tabs, function(t,i) {
17798             if (t.tabId == p.tabId) {
17799                 r = i;
17800                 return false;
17801             }
17802             return null;
17803         });
17804         return r;
17805     },
17806     /**
17807      * show a specific panel
17808      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17809      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17810      */
17811     showPanel : function (pan)
17812     {
17813         if(this.transition || typeof(pan) == 'undefined'){
17814             Roo.log("waiting for the transitionend");
17815             return;
17816         }
17817         
17818         if (typeof(pan) == 'number') {
17819             pan = this.tabs[pan];
17820         }
17821         
17822         if (typeof(pan) == 'string') {
17823             pan = this.getPanelByName(pan);
17824         }
17825         
17826         var cur = this.getActivePanel();
17827         
17828         if(!pan || !cur){
17829             Roo.log('pan or acitve pan is undefined');
17830             return false;
17831         }
17832         
17833         if (pan.tabId == this.getActivePanel().tabId) {
17834             return true;
17835         }
17836         
17837         if (false === cur.fireEvent('beforedeactivate')) {
17838             return false;
17839         }
17840         
17841         if(this.bullets > 0 && !Roo.isTouch){
17842             this.setActiveBullet(this.indexOfPanel(pan));
17843         }
17844         
17845         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17846             
17847             this.transition = true;
17848             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17849             var lr = dir == 'next' ? 'left' : 'right';
17850             pan.el.addClass(dir); // or prev
17851             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17852             cur.el.addClass(lr); // or right
17853             pan.el.addClass(lr);
17854             
17855             var _this = this;
17856             cur.el.on('transitionend', function() {
17857                 Roo.log("trans end?");
17858                 
17859                 pan.el.removeClass([lr,dir]);
17860                 pan.setActive(true);
17861                 
17862                 cur.el.removeClass([lr]);
17863                 cur.setActive(false);
17864                 
17865                 _this.transition = false;
17866                 
17867             }, this, { single:  true } );
17868             
17869             return true;
17870         }
17871         
17872         cur.setActive(false);
17873         pan.setActive(true);
17874         
17875         return true;
17876         
17877     },
17878     showPanelNext : function()
17879     {
17880         var i = this.indexOfPanel(this.getActivePanel());
17881         
17882         if (i >= this.tabs.length - 1 && !this.autoslide) {
17883             return;
17884         }
17885         
17886         if (i >= this.tabs.length - 1 && this.autoslide) {
17887             i = -1;
17888         }
17889         
17890         this.showPanel(this.tabs[i+1]);
17891     },
17892     
17893     showPanelPrev : function()
17894     {
17895         var i = this.indexOfPanel(this.getActivePanel());
17896         
17897         if (i  < 1 && !this.autoslide) {
17898             return;
17899         }
17900         
17901         if (i < 1 && this.autoslide) {
17902             i = this.tabs.length;
17903         }
17904         
17905         this.showPanel(this.tabs[i-1]);
17906     },
17907     
17908     
17909     addBullet: function()
17910     {
17911         if(!this.bullets || Roo.isTouch){
17912             return;
17913         }
17914         var ctr = this.el.select('.carousel-bullets',true).first();
17915         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17916         var bullet = ctr.createChild({
17917             cls : 'bullet bullet-' + i
17918         },ctr.dom.lastChild);
17919         
17920         
17921         var _this = this;
17922         
17923         bullet.on('click', (function(e, el, o, ii, t){
17924
17925             e.preventDefault();
17926
17927             this.showPanel(ii);
17928
17929             if(this.autoslide && this.slideFn){
17930                 clearInterval(this.slideFn);
17931                 this.slideFn = window.setInterval(function() {
17932                     _this.showPanelNext();
17933                 }, this.timer);
17934             }
17935
17936         }).createDelegate(this, [i, bullet], true));
17937                 
17938         
17939     },
17940      
17941     setActiveBullet : function(i)
17942     {
17943         if(Roo.isTouch){
17944             return;
17945         }
17946         
17947         Roo.each(this.el.select('.bullet', true).elements, function(el){
17948             el.removeClass('selected');
17949         });
17950
17951         var bullet = this.el.select('.bullet-' + i, true).first();
17952         
17953         if(!bullet){
17954             return;
17955         }
17956         
17957         bullet.addClass('selected');
17958     }
17959     
17960     
17961   
17962 });
17963
17964  
17965
17966  
17967  
17968 Roo.apply(Roo.bootstrap.TabGroup, {
17969     
17970     groups: {},
17971      /**
17972     * register a Navigation Group
17973     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17974     */
17975     register : function(navgrp)
17976     {
17977         this.groups[navgrp.navId] = navgrp;
17978         
17979     },
17980     /**
17981     * fetch a Navigation Group based on the navigation ID
17982     * if one does not exist , it will get created.
17983     * @param {string} the navgroup to add
17984     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17985     */
17986     get: function(navId) {
17987         if (typeof(this.groups[navId]) == 'undefined') {
17988             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17989         }
17990         return this.groups[navId] ;
17991     }
17992     
17993     
17994     
17995 });
17996
17997  /*
17998  * - LGPL
17999  *
18000  * TabPanel
18001  * 
18002  */
18003
18004 /**
18005  * @class Roo.bootstrap.TabPanel
18006  * @extends Roo.bootstrap.Component
18007  * Bootstrap TabPanel class
18008  * @cfg {Boolean} active panel active
18009  * @cfg {String} html panel content
18010  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18011  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18012  * @cfg {String} href click to link..
18013  * 
18014  * 
18015  * @constructor
18016  * Create a new TabPanel
18017  * @param {Object} config The config object
18018  */
18019
18020 Roo.bootstrap.TabPanel = function(config){
18021     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18022     this.addEvents({
18023         /**
18024              * @event changed
18025              * Fires when the active status changes
18026              * @param {Roo.bootstrap.TabPanel} this
18027              * @param {Boolean} state the new state
18028             
18029          */
18030         'changed': true,
18031         /**
18032              * @event beforedeactivate
18033              * Fires before a tab is de-activated - can be used to do validation on a form.
18034              * @param {Roo.bootstrap.TabPanel} this
18035              * @return {Boolean} false if there is an error
18036             
18037          */
18038         'beforedeactivate': true
18039      });
18040     
18041     this.tabId = this.tabId || Roo.id();
18042   
18043 };
18044
18045 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18046     
18047     active: false,
18048     html: false,
18049     tabId: false,
18050     navId : false,
18051     href : '',
18052     
18053     getAutoCreate : function(){
18054         var cfg = {
18055             tag: 'div',
18056             // item is needed for carousel - not sure if it has any effect otherwise
18057             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18058             html: this.html || ''
18059         };
18060         
18061         if(this.active){
18062             cfg.cls += ' active';
18063         }
18064         
18065         if(this.tabId){
18066             cfg.tabId = this.tabId;
18067         }
18068         
18069         
18070         return cfg;
18071     },
18072     
18073     initEvents:  function()
18074     {
18075         var p = this.parent();
18076         
18077         this.navId = this.navId || p.navId;
18078         
18079         if (typeof(this.navId) != 'undefined') {
18080             // not really needed.. but just in case.. parent should be a NavGroup.
18081             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18082             
18083             tg.register(this);
18084             
18085             var i = tg.tabs.length - 1;
18086             
18087             if(this.active && tg.bullets > 0 && i < tg.bullets){
18088                 tg.setActiveBullet(i);
18089             }
18090         }
18091         
18092         this.el.on('click', this.onClick, this);
18093         
18094         if(Roo.isTouch){
18095             this.el.on("touchstart", this.onTouchStart, this);
18096             this.el.on("touchmove", this.onTouchMove, this);
18097             this.el.on("touchend", this.onTouchEnd, this);
18098         }
18099         
18100     },
18101     
18102     onRender : function(ct, position)
18103     {
18104         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18105     },
18106     
18107     setActive : function(state)
18108     {
18109         Roo.log("panel - set active " + this.tabId + "=" + state);
18110         
18111         this.active = state;
18112         if (!state) {
18113             this.el.removeClass('active');
18114             
18115         } else  if (!this.el.hasClass('active')) {
18116             this.el.addClass('active');
18117         }
18118         
18119         this.fireEvent('changed', this, state);
18120     },
18121     
18122     onClick : function(e)
18123     {
18124         e.preventDefault();
18125         
18126         if(!this.href.length){
18127             return;
18128         }
18129         
18130         window.location.href = this.href;
18131     },
18132     
18133     startX : 0,
18134     startY : 0,
18135     endX : 0,
18136     endY : 0,
18137     swiping : false,
18138     
18139     onTouchStart : function(e)
18140     {
18141         this.swiping = false;
18142         
18143         this.startX = e.browserEvent.touches[0].clientX;
18144         this.startY = e.browserEvent.touches[0].clientY;
18145     },
18146     
18147     onTouchMove : function(e)
18148     {
18149         this.swiping = true;
18150         
18151         this.endX = e.browserEvent.touches[0].clientX;
18152         this.endY = e.browserEvent.touches[0].clientY;
18153     },
18154     
18155     onTouchEnd : function(e)
18156     {
18157         if(!this.swiping){
18158             this.onClick(e);
18159             return;
18160         }
18161         
18162         var tabGroup = this.parent();
18163         
18164         if(this.endX > this.startX){ // swiping right
18165             tabGroup.showPanelPrev();
18166             return;
18167         }
18168         
18169         if(this.startX > this.endX){ // swiping left
18170             tabGroup.showPanelNext();
18171             return;
18172         }
18173     }
18174     
18175     
18176 });
18177  
18178
18179  
18180
18181  /*
18182  * - LGPL
18183  *
18184  * DateField
18185  * 
18186  */
18187
18188 /**
18189  * @class Roo.bootstrap.DateField
18190  * @extends Roo.bootstrap.Input
18191  * Bootstrap DateField class
18192  * @cfg {Number} weekStart default 0
18193  * @cfg {String} viewMode default empty, (months|years)
18194  * @cfg {String} minViewMode default empty, (months|years)
18195  * @cfg {Number} startDate default -Infinity
18196  * @cfg {Number} endDate default Infinity
18197  * @cfg {Boolean} todayHighlight default false
18198  * @cfg {Boolean} todayBtn default false
18199  * @cfg {Boolean} calendarWeeks default false
18200  * @cfg {Object} daysOfWeekDisabled default empty
18201  * @cfg {Boolean} singleMode default false (true | false)
18202  * 
18203  * @cfg {Boolean} keyboardNavigation default true
18204  * @cfg {String} language default en
18205  * 
18206  * @constructor
18207  * Create a new DateField
18208  * @param {Object} config The config object
18209  */
18210
18211 Roo.bootstrap.DateField = function(config){
18212     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18213      this.addEvents({
18214             /**
18215              * @event show
18216              * Fires when this field show.
18217              * @param {Roo.bootstrap.DateField} this
18218              * @param {Mixed} date The date value
18219              */
18220             show : true,
18221             /**
18222              * @event show
18223              * Fires when this field hide.
18224              * @param {Roo.bootstrap.DateField} this
18225              * @param {Mixed} date The date value
18226              */
18227             hide : true,
18228             /**
18229              * @event select
18230              * Fires when select a date.
18231              * @param {Roo.bootstrap.DateField} this
18232              * @param {Mixed} date The date value
18233              */
18234             select : true,
18235             /**
18236              * @event beforeselect
18237              * Fires when before select a date.
18238              * @param {Roo.bootstrap.DateField} this
18239              * @param {Mixed} date The date value
18240              */
18241             beforeselect : true
18242         });
18243 };
18244
18245 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18246     
18247     /**
18248      * @cfg {String} format
18249      * The default date format string which can be overriden for localization support.  The format must be
18250      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18251      */
18252     format : "m/d/y",
18253     /**
18254      * @cfg {String} altFormats
18255      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18256      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18257      */
18258     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18259     
18260     weekStart : 0,
18261     
18262     viewMode : '',
18263     
18264     minViewMode : '',
18265     
18266     todayHighlight : false,
18267     
18268     todayBtn: false,
18269     
18270     language: 'en',
18271     
18272     keyboardNavigation: true,
18273     
18274     calendarWeeks: false,
18275     
18276     startDate: -Infinity,
18277     
18278     endDate: Infinity,
18279     
18280     daysOfWeekDisabled: [],
18281     
18282     _events: [],
18283     
18284     singleMode : false,
18285     
18286     UTCDate: function()
18287     {
18288         return new Date(Date.UTC.apply(Date, arguments));
18289     },
18290     
18291     UTCToday: function()
18292     {
18293         var today = new Date();
18294         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18295     },
18296     
18297     getDate: function() {
18298             var d = this.getUTCDate();
18299             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18300     },
18301     
18302     getUTCDate: function() {
18303             return this.date;
18304     },
18305     
18306     setDate: function(d) {
18307             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18308     },
18309     
18310     setUTCDate: function(d) {
18311             this.date = d;
18312             this.setValue(this.formatDate(this.date));
18313     },
18314         
18315     onRender: function(ct, position)
18316     {
18317         
18318         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18319         
18320         this.language = this.language || 'en';
18321         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18322         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18323         
18324         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18325         this.format = this.format || 'm/d/y';
18326         this.isInline = false;
18327         this.isInput = true;
18328         this.component = this.el.select('.add-on', true).first() || false;
18329         this.component = (this.component && this.component.length === 0) ? false : this.component;
18330         this.hasInput = this.component && this.inputEl().length;
18331         
18332         if (typeof(this.minViewMode === 'string')) {
18333             switch (this.minViewMode) {
18334                 case 'months':
18335                     this.minViewMode = 1;
18336                     break;
18337                 case 'years':
18338                     this.minViewMode = 2;
18339                     break;
18340                 default:
18341                     this.minViewMode = 0;
18342                     break;
18343             }
18344         }
18345         
18346         if (typeof(this.viewMode === 'string')) {
18347             switch (this.viewMode) {
18348                 case 'months':
18349                     this.viewMode = 1;
18350                     break;
18351                 case 'years':
18352                     this.viewMode = 2;
18353                     break;
18354                 default:
18355                     this.viewMode = 0;
18356                     break;
18357             }
18358         }
18359                 
18360         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18361         
18362 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18363         
18364         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18365         
18366         this.picker().on('mousedown', this.onMousedown, this);
18367         this.picker().on('click', this.onClick, this);
18368         
18369         this.picker().addClass('datepicker-dropdown');
18370         
18371         this.startViewMode = this.viewMode;
18372         
18373         if(this.singleMode){
18374             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18375                 v.setVisibilityMode(Roo.Element.DISPLAY);
18376                 v.hide();
18377             });
18378             
18379             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18380                 v.setStyle('width', '189px');
18381             });
18382         }
18383         
18384         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18385             if(!this.calendarWeeks){
18386                 v.remove();
18387                 return;
18388             }
18389             
18390             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18391             v.attr('colspan', function(i, val){
18392                 return parseInt(val) + 1;
18393             });
18394         });
18395                         
18396         
18397         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18398         
18399         this.setStartDate(this.startDate);
18400         this.setEndDate(this.endDate);
18401         
18402         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18403         
18404         this.fillDow();
18405         this.fillMonths();
18406         this.update();
18407         this.showMode();
18408         
18409         if(this.isInline) {
18410             this.show();
18411         }
18412     },
18413     
18414     picker : function()
18415     {
18416         return this.pickerEl;
18417 //        return this.el.select('.datepicker', true).first();
18418     },
18419     
18420     fillDow: function()
18421     {
18422         var dowCnt = this.weekStart;
18423         
18424         var dow = {
18425             tag: 'tr',
18426             cn: [
18427                 
18428             ]
18429         };
18430         
18431         if(this.calendarWeeks){
18432             dow.cn.push({
18433                 tag: 'th',
18434                 cls: 'cw',
18435                 html: '&nbsp;'
18436             })
18437         }
18438         
18439         while (dowCnt < this.weekStart + 7) {
18440             dow.cn.push({
18441                 tag: 'th',
18442                 cls: 'dow',
18443                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18444             });
18445         }
18446         
18447         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18448     },
18449     
18450     fillMonths: function()
18451     {    
18452         var i = 0;
18453         var months = this.picker().select('>.datepicker-months td', true).first();
18454         
18455         months.dom.innerHTML = '';
18456         
18457         while (i < 12) {
18458             var month = {
18459                 tag: 'span',
18460                 cls: 'month',
18461                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18462             };
18463             
18464             months.createChild(month);
18465         }
18466         
18467     },
18468     
18469     update: function()
18470     {
18471         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;
18472         
18473         if (this.date < this.startDate) {
18474             this.viewDate = new Date(this.startDate);
18475         } else if (this.date > this.endDate) {
18476             this.viewDate = new Date(this.endDate);
18477         } else {
18478             this.viewDate = new Date(this.date);
18479         }
18480         
18481         this.fill();
18482     },
18483     
18484     fill: function() 
18485     {
18486         var d = new Date(this.viewDate),
18487                 year = d.getUTCFullYear(),
18488                 month = d.getUTCMonth(),
18489                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18490                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18491                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18492                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18493                 currentDate = this.date && this.date.valueOf(),
18494                 today = this.UTCToday();
18495         
18496         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18497         
18498 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18499         
18500 //        this.picker.select('>tfoot th.today').
18501 //                                              .text(dates[this.language].today)
18502 //                                              .toggle(this.todayBtn !== false);
18503     
18504         this.updateNavArrows();
18505         this.fillMonths();
18506                                                 
18507         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18508         
18509         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18510          
18511         prevMonth.setUTCDate(day);
18512         
18513         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18514         
18515         var nextMonth = new Date(prevMonth);
18516         
18517         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18518         
18519         nextMonth = nextMonth.valueOf();
18520         
18521         var fillMonths = false;
18522         
18523         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18524         
18525         while(prevMonth.valueOf() < nextMonth) {
18526             var clsName = '';
18527             
18528             if (prevMonth.getUTCDay() === this.weekStart) {
18529                 if(fillMonths){
18530                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18531                 }
18532                     
18533                 fillMonths = {
18534                     tag: 'tr',
18535                     cn: []
18536                 };
18537                 
18538                 if(this.calendarWeeks){
18539                     // ISO 8601: First week contains first thursday.
18540                     // ISO also states week starts on Monday, but we can be more abstract here.
18541                     var
18542                     // Start of current week: based on weekstart/current date
18543                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18544                     // Thursday of this week
18545                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18546                     // First Thursday of year, year from thursday
18547                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18548                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18549                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18550                     
18551                     fillMonths.cn.push({
18552                         tag: 'td',
18553                         cls: 'cw',
18554                         html: calWeek
18555                     });
18556                 }
18557             }
18558             
18559             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18560                 clsName += ' old';
18561             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18562                 clsName += ' new';
18563             }
18564             if (this.todayHighlight &&
18565                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18566                 prevMonth.getUTCMonth() == today.getMonth() &&
18567                 prevMonth.getUTCDate() == today.getDate()) {
18568                 clsName += ' today';
18569             }
18570             
18571             if (currentDate && prevMonth.valueOf() === currentDate) {
18572                 clsName += ' active';
18573             }
18574             
18575             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18576                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18577                     clsName += ' disabled';
18578             }
18579             
18580             fillMonths.cn.push({
18581                 tag: 'td',
18582                 cls: 'day ' + clsName,
18583                 html: prevMonth.getDate()
18584             });
18585             
18586             prevMonth.setDate(prevMonth.getDate()+1);
18587         }
18588           
18589         var currentYear = this.date && this.date.getUTCFullYear();
18590         var currentMonth = this.date && this.date.getUTCMonth();
18591         
18592         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18593         
18594         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18595             v.removeClass('active');
18596             
18597             if(currentYear === year && k === currentMonth){
18598                 v.addClass('active');
18599             }
18600             
18601             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18602                 v.addClass('disabled');
18603             }
18604             
18605         });
18606         
18607         
18608         year = parseInt(year/10, 10) * 10;
18609         
18610         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18611         
18612         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18613         
18614         year -= 1;
18615         for (var i = -1; i < 11; i++) {
18616             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18617                 tag: 'span',
18618                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18619                 html: year
18620             });
18621             
18622             year += 1;
18623         }
18624     },
18625     
18626     showMode: function(dir) 
18627     {
18628         if (dir) {
18629             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18630         }
18631         
18632         Roo.each(this.picker().select('>div',true).elements, function(v){
18633             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18634             v.hide();
18635         });
18636         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18637     },
18638     
18639     place: function()
18640     {
18641         if(this.isInline) {
18642             return;
18643         }
18644         
18645         this.picker().removeClass(['bottom', 'top']);
18646         
18647         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18648             /*
18649              * place to the top of element!
18650              *
18651              */
18652             
18653             this.picker().addClass('top');
18654             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18655             
18656             return;
18657         }
18658         
18659         this.picker().addClass('bottom');
18660         
18661         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18662     },
18663     
18664     parseDate : function(value)
18665     {
18666         if(!value || value instanceof Date){
18667             return value;
18668         }
18669         var v = Date.parseDate(value, this.format);
18670         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18671             v = Date.parseDate(value, 'Y-m-d');
18672         }
18673         if(!v && this.altFormats){
18674             if(!this.altFormatsArray){
18675                 this.altFormatsArray = this.altFormats.split("|");
18676             }
18677             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18678                 v = Date.parseDate(value, this.altFormatsArray[i]);
18679             }
18680         }
18681         return v;
18682     },
18683     
18684     formatDate : function(date, fmt)
18685     {   
18686         return (!date || !(date instanceof Date)) ?
18687         date : date.dateFormat(fmt || this.format);
18688     },
18689     
18690     onFocus : function()
18691     {
18692         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18693         this.show();
18694     },
18695     
18696     onBlur : function()
18697     {
18698         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18699         
18700         var d = this.inputEl().getValue();
18701         
18702         this.setValue(d);
18703                 
18704         this.hide();
18705     },
18706     
18707     show : function()
18708     {
18709         this.picker().show();
18710         this.update();
18711         this.place();
18712         
18713         this.fireEvent('show', this, this.date);
18714     },
18715     
18716     hide : function()
18717     {
18718         if(this.isInline) {
18719             return;
18720         }
18721         this.picker().hide();
18722         this.viewMode = this.startViewMode;
18723         this.showMode();
18724         
18725         this.fireEvent('hide', this, this.date);
18726         
18727     },
18728     
18729     onMousedown: function(e)
18730     {
18731         e.stopPropagation();
18732         e.preventDefault();
18733     },
18734     
18735     keyup: function(e)
18736     {
18737         Roo.bootstrap.DateField.superclass.keyup.call(this);
18738         this.update();
18739     },
18740
18741     setValue: function(v)
18742     {
18743         if(this.fireEvent('beforeselect', this, v) !== false){
18744             var d = new Date(this.parseDate(v) ).clearTime();
18745         
18746             if(isNaN(d.getTime())){
18747                 this.date = this.viewDate = '';
18748                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18749                 return;
18750             }
18751
18752             v = this.formatDate(d);
18753
18754             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18755
18756             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18757
18758             this.update();
18759
18760             this.fireEvent('select', this, this.date);
18761         }
18762     },
18763     
18764     getValue: function()
18765     {
18766         return this.formatDate(this.date);
18767     },
18768     
18769     fireKey: function(e)
18770     {
18771         if (!this.picker().isVisible()){
18772             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18773                 this.show();
18774             }
18775             return;
18776         }
18777         
18778         var dateChanged = false,
18779         dir, day, month,
18780         newDate, newViewDate;
18781         
18782         switch(e.keyCode){
18783             case 27: // escape
18784                 this.hide();
18785                 e.preventDefault();
18786                 break;
18787             case 37: // left
18788             case 39: // right
18789                 if (!this.keyboardNavigation) {
18790                     break;
18791                 }
18792                 dir = e.keyCode == 37 ? -1 : 1;
18793                 
18794                 if (e.ctrlKey){
18795                     newDate = this.moveYear(this.date, dir);
18796                     newViewDate = this.moveYear(this.viewDate, dir);
18797                 } else if (e.shiftKey){
18798                     newDate = this.moveMonth(this.date, dir);
18799                     newViewDate = this.moveMonth(this.viewDate, dir);
18800                 } else {
18801                     newDate = new Date(this.date);
18802                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18803                     newViewDate = new Date(this.viewDate);
18804                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18805                 }
18806                 if (this.dateWithinRange(newDate)){
18807                     this.date = newDate;
18808                     this.viewDate = newViewDate;
18809                     this.setValue(this.formatDate(this.date));
18810 //                    this.update();
18811                     e.preventDefault();
18812                     dateChanged = true;
18813                 }
18814                 break;
18815             case 38: // up
18816             case 40: // down
18817                 if (!this.keyboardNavigation) {
18818                     break;
18819                 }
18820                 dir = e.keyCode == 38 ? -1 : 1;
18821                 if (e.ctrlKey){
18822                     newDate = this.moveYear(this.date, dir);
18823                     newViewDate = this.moveYear(this.viewDate, dir);
18824                 } else if (e.shiftKey){
18825                     newDate = this.moveMonth(this.date, dir);
18826                     newViewDate = this.moveMonth(this.viewDate, dir);
18827                 } else {
18828                     newDate = new Date(this.date);
18829                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18830                     newViewDate = new Date(this.viewDate);
18831                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18832                 }
18833                 if (this.dateWithinRange(newDate)){
18834                     this.date = newDate;
18835                     this.viewDate = newViewDate;
18836                     this.setValue(this.formatDate(this.date));
18837 //                    this.update();
18838                     e.preventDefault();
18839                     dateChanged = true;
18840                 }
18841                 break;
18842             case 13: // enter
18843                 this.setValue(this.formatDate(this.date));
18844                 this.hide();
18845                 e.preventDefault();
18846                 break;
18847             case 9: // tab
18848                 this.setValue(this.formatDate(this.date));
18849                 this.hide();
18850                 break;
18851             case 16: // shift
18852             case 17: // ctrl
18853             case 18: // alt
18854                 break;
18855             default :
18856                 this.hide();
18857                 
18858         }
18859     },
18860     
18861     
18862     onClick: function(e) 
18863     {
18864         e.stopPropagation();
18865         e.preventDefault();
18866         
18867         var target = e.getTarget();
18868         
18869         if(target.nodeName.toLowerCase() === 'i'){
18870             target = Roo.get(target).dom.parentNode;
18871         }
18872         
18873         var nodeName = target.nodeName;
18874         var className = target.className;
18875         var html = target.innerHTML;
18876         //Roo.log(nodeName);
18877         
18878         switch(nodeName.toLowerCase()) {
18879             case 'th':
18880                 switch(className) {
18881                     case 'switch':
18882                         this.showMode(1);
18883                         break;
18884                     case 'prev':
18885                     case 'next':
18886                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18887                         switch(this.viewMode){
18888                                 case 0:
18889                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18890                                         break;
18891                                 case 1:
18892                                 case 2:
18893                                         this.viewDate = this.moveYear(this.viewDate, dir);
18894                                         break;
18895                         }
18896                         this.fill();
18897                         break;
18898                     case 'today':
18899                         var date = new Date();
18900                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18901 //                        this.fill()
18902                         this.setValue(this.formatDate(this.date));
18903                         
18904                         this.hide();
18905                         break;
18906                 }
18907                 break;
18908             case 'span':
18909                 if (className.indexOf('disabled') < 0) {
18910                     this.viewDate.setUTCDate(1);
18911                     if (className.indexOf('month') > -1) {
18912                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18913                     } else {
18914                         var year = parseInt(html, 10) || 0;
18915                         this.viewDate.setUTCFullYear(year);
18916                         
18917                     }
18918                     
18919                     if(this.singleMode){
18920                         this.setValue(this.formatDate(this.viewDate));
18921                         this.hide();
18922                         return;
18923                     }
18924                     
18925                     this.showMode(-1);
18926                     this.fill();
18927                 }
18928                 break;
18929                 
18930             case 'td':
18931                 //Roo.log(className);
18932                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18933                     var day = parseInt(html, 10) || 1;
18934                     var year = this.viewDate.getUTCFullYear(),
18935                         month = this.viewDate.getUTCMonth();
18936
18937                     if (className.indexOf('old') > -1) {
18938                         if(month === 0 ){
18939                             month = 11;
18940                             year -= 1;
18941                         }else{
18942                             month -= 1;
18943                         }
18944                     } else if (className.indexOf('new') > -1) {
18945                         if (month == 11) {
18946                             month = 0;
18947                             year += 1;
18948                         } else {
18949                             month += 1;
18950                         }
18951                     }
18952                     //Roo.log([year,month,day]);
18953                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18954                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18955 //                    this.fill();
18956                     //Roo.log(this.formatDate(this.date));
18957                     this.setValue(this.formatDate(this.date));
18958                     this.hide();
18959                 }
18960                 break;
18961         }
18962     },
18963     
18964     setStartDate: function(startDate)
18965     {
18966         this.startDate = startDate || -Infinity;
18967         if (this.startDate !== -Infinity) {
18968             this.startDate = this.parseDate(this.startDate);
18969         }
18970         this.update();
18971         this.updateNavArrows();
18972     },
18973
18974     setEndDate: function(endDate)
18975     {
18976         this.endDate = endDate || Infinity;
18977         if (this.endDate !== Infinity) {
18978             this.endDate = this.parseDate(this.endDate);
18979         }
18980         this.update();
18981         this.updateNavArrows();
18982     },
18983     
18984     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18985     {
18986         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18987         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18988             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18989         }
18990         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18991             return parseInt(d, 10);
18992         });
18993         this.update();
18994         this.updateNavArrows();
18995     },
18996     
18997     updateNavArrows: function() 
18998     {
18999         if(this.singleMode){
19000             return;
19001         }
19002         
19003         var d = new Date(this.viewDate),
19004         year = d.getUTCFullYear(),
19005         month = d.getUTCMonth();
19006         
19007         Roo.each(this.picker().select('.prev', true).elements, function(v){
19008             v.show();
19009             switch (this.viewMode) {
19010                 case 0:
19011
19012                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19013                         v.hide();
19014                     }
19015                     break;
19016                 case 1:
19017                 case 2:
19018                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19019                         v.hide();
19020                     }
19021                     break;
19022             }
19023         });
19024         
19025         Roo.each(this.picker().select('.next', true).elements, function(v){
19026             v.show();
19027             switch (this.viewMode) {
19028                 case 0:
19029
19030                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19031                         v.hide();
19032                     }
19033                     break;
19034                 case 1:
19035                 case 2:
19036                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19037                         v.hide();
19038                     }
19039                     break;
19040             }
19041         })
19042     },
19043     
19044     moveMonth: function(date, dir)
19045     {
19046         if (!dir) {
19047             return date;
19048         }
19049         var new_date = new Date(date.valueOf()),
19050         day = new_date.getUTCDate(),
19051         month = new_date.getUTCMonth(),
19052         mag = Math.abs(dir),
19053         new_month, test;
19054         dir = dir > 0 ? 1 : -1;
19055         if (mag == 1){
19056             test = dir == -1
19057             // If going back one month, make sure month is not current month
19058             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19059             ? function(){
19060                 return new_date.getUTCMonth() == month;
19061             }
19062             // If going forward one month, make sure month is as expected
19063             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19064             : function(){
19065                 return new_date.getUTCMonth() != new_month;
19066             };
19067             new_month = month + dir;
19068             new_date.setUTCMonth(new_month);
19069             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19070             if (new_month < 0 || new_month > 11) {
19071                 new_month = (new_month + 12) % 12;
19072             }
19073         } else {
19074             // For magnitudes >1, move one month at a time...
19075             for (var i=0; i<mag; i++) {
19076                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19077                 new_date = this.moveMonth(new_date, dir);
19078             }
19079             // ...then reset the day, keeping it in the new month
19080             new_month = new_date.getUTCMonth();
19081             new_date.setUTCDate(day);
19082             test = function(){
19083                 return new_month != new_date.getUTCMonth();
19084             };
19085         }
19086         // Common date-resetting loop -- if date is beyond end of month, make it
19087         // end of month
19088         while (test()){
19089             new_date.setUTCDate(--day);
19090             new_date.setUTCMonth(new_month);
19091         }
19092         return new_date;
19093     },
19094
19095     moveYear: function(date, dir)
19096     {
19097         return this.moveMonth(date, dir*12);
19098     },
19099
19100     dateWithinRange: function(date)
19101     {
19102         return date >= this.startDate && date <= this.endDate;
19103     },
19104
19105     
19106     remove: function() 
19107     {
19108         this.picker().remove();
19109     },
19110     
19111     validateValue : function(value)
19112     {
19113         if(value.length < 1)  {
19114             if(this.allowBlank){
19115                 return true;
19116             }
19117             return false;
19118         }
19119         
19120         if(value.length < this.minLength){
19121             return false;
19122         }
19123         if(value.length > this.maxLength){
19124             return false;
19125         }
19126         if(this.vtype){
19127             var vt = Roo.form.VTypes;
19128             if(!vt[this.vtype](value, this)){
19129                 return false;
19130             }
19131         }
19132         if(typeof this.validator == "function"){
19133             var msg = this.validator(value);
19134             if(msg !== true){
19135                 return false;
19136             }
19137         }
19138         
19139         if(this.regex && !this.regex.test(value)){
19140             return false;
19141         }
19142         
19143         if(typeof(this.parseDate(value)) == 'undefined'){
19144             return false;
19145         }
19146         
19147         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19148             return false;
19149         }      
19150         
19151         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19152             return false;
19153         } 
19154         
19155         
19156         return true;
19157     }
19158    
19159 });
19160
19161 Roo.apply(Roo.bootstrap.DateField,  {
19162     
19163     head : {
19164         tag: 'thead',
19165         cn: [
19166         {
19167             tag: 'tr',
19168             cn: [
19169             {
19170                 tag: 'th',
19171                 cls: 'prev',
19172                 html: '<i class="fa fa-arrow-left"/>'
19173             },
19174             {
19175                 tag: 'th',
19176                 cls: 'switch',
19177                 colspan: '5'
19178             },
19179             {
19180                 tag: 'th',
19181                 cls: 'next',
19182                 html: '<i class="fa fa-arrow-right"/>'
19183             }
19184
19185             ]
19186         }
19187         ]
19188     },
19189     
19190     content : {
19191         tag: 'tbody',
19192         cn: [
19193         {
19194             tag: 'tr',
19195             cn: [
19196             {
19197                 tag: 'td',
19198                 colspan: '7'
19199             }
19200             ]
19201         }
19202         ]
19203     },
19204     
19205     footer : {
19206         tag: 'tfoot',
19207         cn: [
19208         {
19209             tag: 'tr',
19210             cn: [
19211             {
19212                 tag: 'th',
19213                 colspan: '7',
19214                 cls: 'today'
19215             }
19216                     
19217             ]
19218         }
19219         ]
19220     },
19221     
19222     dates:{
19223         en: {
19224             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19225             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19226             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19227             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19228             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19229             today: "Today"
19230         }
19231     },
19232     
19233     modes: [
19234     {
19235         clsName: 'days',
19236         navFnc: 'Month',
19237         navStep: 1
19238     },
19239     {
19240         clsName: 'months',
19241         navFnc: 'FullYear',
19242         navStep: 1
19243     },
19244     {
19245         clsName: 'years',
19246         navFnc: 'FullYear',
19247         navStep: 10
19248     }]
19249 });
19250
19251 Roo.apply(Roo.bootstrap.DateField,  {
19252   
19253     template : {
19254         tag: 'div',
19255         cls: 'datepicker dropdown-menu roo-dynamic',
19256         cn: [
19257         {
19258             tag: 'div',
19259             cls: 'datepicker-days',
19260             cn: [
19261             {
19262                 tag: 'table',
19263                 cls: 'table-condensed',
19264                 cn:[
19265                 Roo.bootstrap.DateField.head,
19266                 {
19267                     tag: 'tbody'
19268                 },
19269                 Roo.bootstrap.DateField.footer
19270                 ]
19271             }
19272             ]
19273         },
19274         {
19275             tag: 'div',
19276             cls: 'datepicker-months',
19277             cn: [
19278             {
19279                 tag: 'table',
19280                 cls: 'table-condensed',
19281                 cn:[
19282                 Roo.bootstrap.DateField.head,
19283                 Roo.bootstrap.DateField.content,
19284                 Roo.bootstrap.DateField.footer
19285                 ]
19286             }
19287             ]
19288         },
19289         {
19290             tag: 'div',
19291             cls: 'datepicker-years',
19292             cn: [
19293             {
19294                 tag: 'table',
19295                 cls: 'table-condensed',
19296                 cn:[
19297                 Roo.bootstrap.DateField.head,
19298                 Roo.bootstrap.DateField.content,
19299                 Roo.bootstrap.DateField.footer
19300                 ]
19301             }
19302             ]
19303         }
19304         ]
19305     }
19306 });
19307
19308  
19309
19310  /*
19311  * - LGPL
19312  *
19313  * TimeField
19314  * 
19315  */
19316
19317 /**
19318  * @class Roo.bootstrap.TimeField
19319  * @extends Roo.bootstrap.Input
19320  * Bootstrap DateField class
19321  * 
19322  * 
19323  * @constructor
19324  * Create a new TimeField
19325  * @param {Object} config The config object
19326  */
19327
19328 Roo.bootstrap.TimeField = function(config){
19329     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19330     this.addEvents({
19331             /**
19332              * @event show
19333              * Fires when this field show.
19334              * @param {Roo.bootstrap.DateField} thisthis
19335              * @param {Mixed} date The date value
19336              */
19337             show : true,
19338             /**
19339              * @event show
19340              * Fires when this field hide.
19341              * @param {Roo.bootstrap.DateField} this
19342              * @param {Mixed} date The date value
19343              */
19344             hide : true,
19345             /**
19346              * @event select
19347              * Fires when select a date.
19348              * @param {Roo.bootstrap.DateField} this
19349              * @param {Mixed} date The date value
19350              */
19351             select : true
19352         });
19353 };
19354
19355 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19356     
19357     /**
19358      * @cfg {String} format
19359      * The default time format string which can be overriden for localization support.  The format must be
19360      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19361      */
19362     format : "H:i",
19363        
19364     onRender: function(ct, position)
19365     {
19366         
19367         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19368                 
19369         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19370         
19371         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372         
19373         this.pop = this.picker().select('>.datepicker-time',true).first();
19374         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19375         
19376         this.picker().on('mousedown', this.onMousedown, this);
19377         this.picker().on('click', this.onClick, this);
19378         
19379         this.picker().addClass('datepicker-dropdown');
19380     
19381         this.fillTime();
19382         this.update();
19383             
19384         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19385         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19386         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19387         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19388         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19389         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19390
19391     },
19392     
19393     fireKey: function(e){
19394         if (!this.picker().isVisible()){
19395             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19396                 this.show();
19397             }
19398             return;
19399         }
19400
19401         e.preventDefault();
19402         
19403         switch(e.keyCode){
19404             case 27: // escape
19405                 this.hide();
19406                 break;
19407             case 37: // left
19408             case 39: // right
19409                 this.onTogglePeriod();
19410                 break;
19411             case 38: // up
19412                 this.onIncrementMinutes();
19413                 break;
19414             case 40: // down
19415                 this.onDecrementMinutes();
19416                 break;
19417             case 13: // enter
19418             case 9: // tab
19419                 this.setTime();
19420                 break;
19421         }
19422     },
19423     
19424     onClick: function(e) {
19425         e.stopPropagation();
19426         e.preventDefault();
19427     },
19428     
19429     picker : function()
19430     {
19431         return this.el.select('.datepicker', true).first();
19432     },
19433     
19434     fillTime: function()
19435     {    
19436         var time = this.pop.select('tbody', true).first();
19437         
19438         time.dom.innerHTML = '';
19439         
19440         time.createChild({
19441             tag: 'tr',
19442             cn: [
19443                 {
19444                     tag: 'td',
19445                     cn: [
19446                         {
19447                             tag: 'a',
19448                             href: '#',
19449                             cls: 'btn',
19450                             cn: [
19451                                 {
19452                                     tag: 'span',
19453                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19454                                 }
19455                             ]
19456                         } 
19457                     ]
19458                 },
19459                 {
19460                     tag: 'td',
19461                     cls: 'separator'
19462                 },
19463                 {
19464                     tag: 'td',
19465                     cn: [
19466                         {
19467                             tag: 'a',
19468                             href: '#',
19469                             cls: 'btn',
19470                             cn: [
19471                                 {
19472                                     tag: 'span',
19473                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19474                                 }
19475                             ]
19476                         }
19477                     ]
19478                 },
19479                 {
19480                     tag: 'td',
19481                     cls: 'separator'
19482                 }
19483             ]
19484         });
19485         
19486         time.createChild({
19487             tag: 'tr',
19488             cn: [
19489                 {
19490                     tag: 'td',
19491                     cn: [
19492                         {
19493                             tag: 'span',
19494                             cls: 'timepicker-hour',
19495                             html: '00'
19496                         }  
19497                     ]
19498                 },
19499                 {
19500                     tag: 'td',
19501                     cls: 'separator',
19502                     html: ':'
19503                 },
19504                 {
19505                     tag: 'td',
19506                     cn: [
19507                         {
19508                             tag: 'span',
19509                             cls: 'timepicker-minute',
19510                             html: '00'
19511                         }  
19512                     ]
19513                 },
19514                 {
19515                     tag: 'td',
19516                     cls: 'separator'
19517                 },
19518                 {
19519                     tag: 'td',
19520                     cn: [
19521                         {
19522                             tag: 'button',
19523                             type: 'button',
19524                             cls: 'btn btn-primary period',
19525                             html: 'AM'
19526                             
19527                         }
19528                     ]
19529                 }
19530             ]
19531         });
19532         
19533         time.createChild({
19534             tag: 'tr',
19535             cn: [
19536                 {
19537                     tag: 'td',
19538                     cn: [
19539                         {
19540                             tag: 'a',
19541                             href: '#',
19542                             cls: 'btn',
19543                             cn: [
19544                                 {
19545                                     tag: 'span',
19546                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19547                                 }
19548                             ]
19549                         }
19550                     ]
19551                 },
19552                 {
19553                     tag: 'td',
19554                     cls: 'separator'
19555                 },
19556                 {
19557                     tag: 'td',
19558                     cn: [
19559                         {
19560                             tag: 'a',
19561                             href: '#',
19562                             cls: 'btn',
19563                             cn: [
19564                                 {
19565                                     tag: 'span',
19566                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19567                                 }
19568                             ]
19569                         }
19570                     ]
19571                 },
19572                 {
19573                     tag: 'td',
19574                     cls: 'separator'
19575                 }
19576             ]
19577         });
19578         
19579     },
19580     
19581     update: function()
19582     {
19583         
19584         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19585         
19586         this.fill();
19587     },
19588     
19589     fill: function() 
19590     {
19591         var hours = this.time.getHours();
19592         var minutes = this.time.getMinutes();
19593         var period = 'AM';
19594         
19595         if(hours > 11){
19596             period = 'PM';
19597         }
19598         
19599         if(hours == 0){
19600             hours = 12;
19601         }
19602         
19603         
19604         if(hours > 12){
19605             hours = hours - 12;
19606         }
19607         
19608         if(hours < 10){
19609             hours = '0' + hours;
19610         }
19611         
19612         if(minutes < 10){
19613             minutes = '0' + minutes;
19614         }
19615         
19616         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19617         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19618         this.pop.select('button', true).first().dom.innerHTML = period;
19619         
19620     },
19621     
19622     place: function()
19623     {   
19624         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19625         
19626         var cls = ['bottom'];
19627         
19628         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19629             cls.pop();
19630             cls.push('top');
19631         }
19632         
19633         cls.push('right');
19634         
19635         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19636             cls.pop();
19637             cls.push('left');
19638         }
19639         
19640         this.picker().addClass(cls.join('-'));
19641         
19642         var _this = this;
19643         
19644         Roo.each(cls, function(c){
19645             if(c == 'bottom'){
19646                 _this.picker().setTop(_this.inputEl().getHeight());
19647                 return;
19648             }
19649             if(c == 'top'){
19650                 _this.picker().setTop(0 - _this.picker().getHeight());
19651                 return;
19652             }
19653             
19654             if(c == 'left'){
19655                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19656                 return;
19657             }
19658             if(c == 'right'){
19659                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19660                 return;
19661             }
19662         });
19663         
19664     },
19665   
19666     onFocus : function()
19667     {
19668         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19669         this.show();
19670     },
19671     
19672     onBlur : function()
19673     {
19674         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19675         this.hide();
19676     },
19677     
19678     show : function()
19679     {
19680         this.picker().show();
19681         this.pop.show();
19682         this.update();
19683         this.place();
19684         
19685         this.fireEvent('show', this, this.date);
19686     },
19687     
19688     hide : function()
19689     {
19690         this.picker().hide();
19691         this.pop.hide();
19692         
19693         this.fireEvent('hide', this, this.date);
19694     },
19695     
19696     setTime : function()
19697     {
19698         this.hide();
19699         this.setValue(this.time.format(this.format));
19700         
19701         this.fireEvent('select', this, this.date);
19702         
19703         
19704     },
19705     
19706     onMousedown: function(e){
19707         e.stopPropagation();
19708         e.preventDefault();
19709     },
19710     
19711     onIncrementHours: function()
19712     {
19713         Roo.log('onIncrementHours');
19714         this.time = this.time.add(Date.HOUR, 1);
19715         this.update();
19716         
19717     },
19718     
19719     onDecrementHours: function()
19720     {
19721         Roo.log('onDecrementHours');
19722         this.time = this.time.add(Date.HOUR, -1);
19723         this.update();
19724     },
19725     
19726     onIncrementMinutes: function()
19727     {
19728         Roo.log('onIncrementMinutes');
19729         this.time = this.time.add(Date.MINUTE, 1);
19730         this.update();
19731     },
19732     
19733     onDecrementMinutes: function()
19734     {
19735         Roo.log('onDecrementMinutes');
19736         this.time = this.time.add(Date.MINUTE, -1);
19737         this.update();
19738     },
19739     
19740     onTogglePeriod: function()
19741     {
19742         Roo.log('onTogglePeriod');
19743         this.time = this.time.add(Date.HOUR, 12);
19744         this.update();
19745     }
19746     
19747    
19748 });
19749
19750 Roo.apply(Roo.bootstrap.TimeField,  {
19751     
19752     content : {
19753         tag: 'tbody',
19754         cn: [
19755             {
19756                 tag: 'tr',
19757                 cn: [
19758                 {
19759                     tag: 'td',
19760                     colspan: '7'
19761                 }
19762                 ]
19763             }
19764         ]
19765     },
19766     
19767     footer : {
19768         tag: 'tfoot',
19769         cn: [
19770             {
19771                 tag: 'tr',
19772                 cn: [
19773                 {
19774                     tag: 'th',
19775                     colspan: '7',
19776                     cls: '',
19777                     cn: [
19778                         {
19779                             tag: 'button',
19780                             cls: 'btn btn-info ok',
19781                             html: 'OK'
19782                         }
19783                     ]
19784                 }
19785
19786                 ]
19787             }
19788         ]
19789     }
19790 });
19791
19792 Roo.apply(Roo.bootstrap.TimeField,  {
19793   
19794     template : {
19795         tag: 'div',
19796         cls: 'datepicker dropdown-menu',
19797         cn: [
19798             {
19799                 tag: 'div',
19800                 cls: 'datepicker-time',
19801                 cn: [
19802                 {
19803                     tag: 'table',
19804                     cls: 'table-condensed',
19805                     cn:[
19806                     Roo.bootstrap.TimeField.content,
19807                     Roo.bootstrap.TimeField.footer
19808                     ]
19809                 }
19810                 ]
19811             }
19812         ]
19813     }
19814 });
19815
19816  
19817
19818  /*
19819  * - LGPL
19820  *
19821  * MonthField
19822  * 
19823  */
19824
19825 /**
19826  * @class Roo.bootstrap.MonthField
19827  * @extends Roo.bootstrap.Input
19828  * Bootstrap MonthField class
19829  * 
19830  * @cfg {String} language default en
19831  * 
19832  * @constructor
19833  * Create a new MonthField
19834  * @param {Object} config The config object
19835  */
19836
19837 Roo.bootstrap.MonthField = function(config){
19838     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19839     
19840     this.addEvents({
19841         /**
19842          * @event show
19843          * Fires when this field show.
19844          * @param {Roo.bootstrap.MonthField} this
19845          * @param {Mixed} date The date value
19846          */
19847         show : true,
19848         /**
19849          * @event show
19850          * Fires when this field hide.
19851          * @param {Roo.bootstrap.MonthField} this
19852          * @param {Mixed} date The date value
19853          */
19854         hide : true,
19855         /**
19856          * @event select
19857          * Fires when select a date.
19858          * @param {Roo.bootstrap.MonthField} this
19859          * @param {String} oldvalue The old value
19860          * @param {String} newvalue The new value
19861          */
19862         select : true
19863     });
19864 };
19865
19866 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19867     
19868     onRender: function(ct, position)
19869     {
19870         
19871         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19872         
19873         this.language = this.language || 'en';
19874         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19875         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19876         
19877         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19878         this.isInline = false;
19879         this.isInput = true;
19880         this.component = this.el.select('.add-on', true).first() || false;
19881         this.component = (this.component && this.component.length === 0) ? false : this.component;
19882         this.hasInput = this.component && this.inputEL().length;
19883         
19884         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19885         
19886         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19887         
19888         this.picker().on('mousedown', this.onMousedown, this);
19889         this.picker().on('click', this.onClick, this);
19890         
19891         this.picker().addClass('datepicker-dropdown');
19892         
19893         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19894             v.setStyle('width', '189px');
19895         });
19896         
19897         this.fillMonths();
19898         
19899         this.update();
19900         
19901         if(this.isInline) {
19902             this.show();
19903         }
19904         
19905     },
19906     
19907     setValue: function(v, suppressEvent)
19908     {   
19909         var o = this.getValue();
19910         
19911         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19912         
19913         this.update();
19914
19915         if(suppressEvent !== true){
19916             this.fireEvent('select', this, o, v);
19917         }
19918         
19919     },
19920     
19921     getValue: function()
19922     {
19923         return this.value;
19924     },
19925     
19926     onClick: function(e) 
19927     {
19928         e.stopPropagation();
19929         e.preventDefault();
19930         
19931         var target = e.getTarget();
19932         
19933         if(target.nodeName.toLowerCase() === 'i'){
19934             target = Roo.get(target).dom.parentNode;
19935         }
19936         
19937         var nodeName = target.nodeName;
19938         var className = target.className;
19939         var html = target.innerHTML;
19940         
19941         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19942             return;
19943         }
19944         
19945         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19946         
19947         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19948         
19949         this.hide();
19950                         
19951     },
19952     
19953     picker : function()
19954     {
19955         return this.pickerEl;
19956     },
19957     
19958     fillMonths: function()
19959     {    
19960         var i = 0;
19961         var months = this.picker().select('>.datepicker-months td', true).first();
19962         
19963         months.dom.innerHTML = '';
19964         
19965         while (i < 12) {
19966             var month = {
19967                 tag: 'span',
19968                 cls: 'month',
19969                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19970             };
19971             
19972             months.createChild(month);
19973         }
19974         
19975     },
19976     
19977     update: function()
19978     {
19979         var _this = this;
19980         
19981         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19982             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19983         }
19984         
19985         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19986             e.removeClass('active');
19987             
19988             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19989                 e.addClass('active');
19990             }
19991         })
19992     },
19993     
19994     place: function()
19995     {
19996         if(this.isInline) {
19997             return;
19998         }
19999         
20000         this.picker().removeClass(['bottom', 'top']);
20001         
20002         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20003             /*
20004              * place to the top of element!
20005              *
20006              */
20007             
20008             this.picker().addClass('top');
20009             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20010             
20011             return;
20012         }
20013         
20014         this.picker().addClass('bottom');
20015         
20016         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20017     },
20018     
20019     onFocus : function()
20020     {
20021         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20022         this.show();
20023     },
20024     
20025     onBlur : function()
20026     {
20027         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20028         
20029         var d = this.inputEl().getValue();
20030         
20031         this.setValue(d);
20032                 
20033         this.hide();
20034     },
20035     
20036     show : function()
20037     {
20038         this.picker().show();
20039         this.picker().select('>.datepicker-months', true).first().show();
20040         this.update();
20041         this.place();
20042         
20043         this.fireEvent('show', this, this.date);
20044     },
20045     
20046     hide : function()
20047     {
20048         if(this.isInline) {
20049             return;
20050         }
20051         this.picker().hide();
20052         this.fireEvent('hide', this, this.date);
20053         
20054     },
20055     
20056     onMousedown: function(e)
20057     {
20058         e.stopPropagation();
20059         e.preventDefault();
20060     },
20061     
20062     keyup: function(e)
20063     {
20064         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20065         this.update();
20066     },
20067
20068     fireKey: function(e)
20069     {
20070         if (!this.picker().isVisible()){
20071             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20072                 this.show();
20073             }
20074             return;
20075         }
20076         
20077         var dir;
20078         
20079         switch(e.keyCode){
20080             case 27: // escape
20081                 this.hide();
20082                 e.preventDefault();
20083                 break;
20084             case 37: // left
20085             case 39: // right
20086                 dir = e.keyCode == 37 ? -1 : 1;
20087                 
20088                 this.vIndex = this.vIndex + dir;
20089                 
20090                 if(this.vIndex < 0){
20091                     this.vIndex = 0;
20092                 }
20093                 
20094                 if(this.vIndex > 11){
20095                     this.vIndex = 11;
20096                 }
20097                 
20098                 if(isNaN(this.vIndex)){
20099                     this.vIndex = 0;
20100                 }
20101                 
20102                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20103                 
20104                 break;
20105             case 38: // up
20106             case 40: // down
20107                 
20108                 dir = e.keyCode == 38 ? -1 : 1;
20109                 
20110                 this.vIndex = this.vIndex + dir * 4;
20111                 
20112                 if(this.vIndex < 0){
20113                     this.vIndex = 0;
20114                 }
20115                 
20116                 if(this.vIndex > 11){
20117                     this.vIndex = 11;
20118                 }
20119                 
20120                 if(isNaN(this.vIndex)){
20121                     this.vIndex = 0;
20122                 }
20123                 
20124                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20125                 break;
20126                 
20127             case 13: // enter
20128                 
20129                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20130                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20131                 }
20132                 
20133                 this.hide();
20134                 e.preventDefault();
20135                 break;
20136             case 9: // tab
20137                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20138                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20139                 }
20140                 this.hide();
20141                 break;
20142             case 16: // shift
20143             case 17: // ctrl
20144             case 18: // alt
20145                 break;
20146             default :
20147                 this.hide();
20148                 
20149         }
20150     },
20151     
20152     remove: function() 
20153     {
20154         this.picker().remove();
20155     }
20156    
20157 });
20158
20159 Roo.apply(Roo.bootstrap.MonthField,  {
20160     
20161     content : {
20162         tag: 'tbody',
20163         cn: [
20164         {
20165             tag: 'tr',
20166             cn: [
20167             {
20168                 tag: 'td',
20169                 colspan: '7'
20170             }
20171             ]
20172         }
20173         ]
20174     },
20175     
20176     dates:{
20177         en: {
20178             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20179             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20180         }
20181     }
20182 });
20183
20184 Roo.apply(Roo.bootstrap.MonthField,  {
20185   
20186     template : {
20187         tag: 'div',
20188         cls: 'datepicker dropdown-menu roo-dynamic',
20189         cn: [
20190             {
20191                 tag: 'div',
20192                 cls: 'datepicker-months',
20193                 cn: [
20194                 {
20195                     tag: 'table',
20196                     cls: 'table-condensed',
20197                     cn:[
20198                         Roo.bootstrap.DateField.content
20199                     ]
20200                 }
20201                 ]
20202             }
20203         ]
20204     }
20205 });
20206
20207  
20208
20209  
20210  /*
20211  * - LGPL
20212  *
20213  * CheckBox
20214  * 
20215  */
20216
20217 /**
20218  * @class Roo.bootstrap.CheckBox
20219  * @extends Roo.bootstrap.Input
20220  * Bootstrap CheckBox class
20221  * 
20222  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20223  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20224  * @cfg {String} boxLabel The text that appears beside the checkbox
20225  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20226  * @cfg {Boolean} checked initnal the element
20227  * @cfg {Boolean} inline inline the element (default false)
20228  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20229  * @cfg {String} tooltip label tooltip
20230  * 
20231  * @constructor
20232  * Create a new CheckBox
20233  * @param {Object} config The config object
20234  */
20235
20236 Roo.bootstrap.CheckBox = function(config){
20237     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20238    
20239     this.addEvents({
20240         /**
20241         * @event check
20242         * Fires when the element is checked or unchecked.
20243         * @param {Roo.bootstrap.CheckBox} this This input
20244         * @param {Boolean} checked The new checked value
20245         */
20246        check : true,
20247        /**
20248         * @event click
20249         * Fires when the element is click.
20250         * @param {Roo.bootstrap.CheckBox} this This input
20251         */
20252        click : true
20253     });
20254     
20255 };
20256
20257 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20258   
20259     inputType: 'checkbox',
20260     inputValue: 1,
20261     valueOff: 0,
20262     boxLabel: false,
20263     checked: false,
20264     weight : false,
20265     inline: false,
20266     tooltip : '',
20267     
20268     getAutoCreate : function()
20269     {
20270         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20271         
20272         var id = Roo.id();
20273         
20274         var cfg = {};
20275         
20276         cfg.cls = 'form-group ' + this.inputType; //input-group
20277         
20278         if(this.inline){
20279             cfg.cls += ' ' + this.inputType + '-inline';
20280         }
20281         
20282         var input =  {
20283             tag: 'input',
20284             id : id,
20285             type : this.inputType,
20286             value : this.inputValue,
20287             cls : 'roo-' + this.inputType, //'form-box',
20288             placeholder : this.placeholder || ''
20289             
20290         };
20291         
20292         if(this.inputType != 'radio'){
20293             var hidden =  {
20294                 tag: 'input',
20295                 type : 'hidden',
20296                 cls : 'roo-hidden-value',
20297                 value : this.checked ? this.inputValue : this.valueOff
20298             };
20299         }
20300         
20301             
20302         if (this.weight) { // Validity check?
20303             cfg.cls += " " + this.inputType + "-" + this.weight;
20304         }
20305         
20306         if (this.disabled) {
20307             input.disabled=true;
20308         }
20309         
20310         if(this.checked){
20311             input.checked = this.checked;
20312         }
20313         
20314         if (this.name) {
20315             
20316             input.name = this.name;
20317             
20318             if(this.inputType != 'radio'){
20319                 hidden.name = this.name;
20320                 input.name = '_hidden_' + this.name;
20321             }
20322         }
20323         
20324         if (this.size) {
20325             input.cls += ' input-' + this.size;
20326         }
20327         
20328         var settings=this;
20329         
20330         ['xs','sm','md','lg'].map(function(size){
20331             if (settings[size]) {
20332                 cfg.cls += ' col-' + size + '-' + settings[size];
20333             }
20334         });
20335         
20336         var inputblock = input;
20337          
20338         if (this.before || this.after) {
20339             
20340             inputblock = {
20341                 cls : 'input-group',
20342                 cn :  [] 
20343             };
20344             
20345             if (this.before) {
20346                 inputblock.cn.push({
20347                     tag :'span',
20348                     cls : 'input-group-addon',
20349                     html : this.before
20350                 });
20351             }
20352             
20353             inputblock.cn.push(input);
20354             
20355             if(this.inputType != 'radio'){
20356                 inputblock.cn.push(hidden);
20357             }
20358             
20359             if (this.after) {
20360                 inputblock.cn.push({
20361                     tag :'span',
20362                     cls : 'input-group-addon',
20363                     html : this.after
20364                 });
20365             }
20366             
20367         }
20368         
20369         if (align ==='left' && this.fieldLabel.length) {
20370 //                Roo.log("left and has label");
20371             cfg.cn = [
20372                 {
20373                     tag: 'label',
20374                     'for' :  id,
20375                     cls : 'control-label',
20376                     html : this.fieldLabel
20377                 },
20378                 {
20379                     cls : "", 
20380                     cn: [
20381                         inputblock
20382                     ]
20383                 }
20384             ];
20385             
20386             if(this.labelWidth > 12){
20387                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20388             }
20389             
20390             if(this.labelWidth < 13 && this.labelmd == 0){
20391                 this.labelmd = this.labelWidth;
20392             }
20393             
20394             if(this.labellg > 0){
20395                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20396                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20397             }
20398             
20399             if(this.labelmd > 0){
20400                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20401                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20402             }
20403             
20404             if(this.labelsm > 0){
20405                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20406                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20407             }
20408             
20409             if(this.labelxs > 0){
20410                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20411                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20412             }
20413             
20414         } else if ( this.fieldLabel.length) {
20415 //                Roo.log(" label");
20416                 cfg.cn = [
20417                    
20418                     {
20419                         tag: this.boxLabel ? 'span' : 'label',
20420                         'for': id,
20421                         cls: 'control-label box-input-label',
20422                         //cls : 'input-group-addon',
20423                         html : this.fieldLabel
20424                     },
20425                     
20426                     inputblock
20427                     
20428                 ];
20429
20430         } else {
20431             
20432 //                Roo.log(" no label && no align");
20433                 cfg.cn = [  inputblock ] ;
20434                 
20435                 
20436         }
20437         
20438         if(this.boxLabel){
20439              var boxLabelCfg = {
20440                 tag: 'label',
20441                 //'for': id, // box label is handled by onclick - so no for...
20442                 cls: 'box-label',
20443                 html: this.boxLabel
20444             };
20445             
20446             if(this.tooltip){
20447                 boxLabelCfg.tooltip = this.tooltip;
20448             }
20449              
20450             cfg.cn.push(boxLabelCfg);
20451         }
20452         
20453         if(this.inputType != 'radio'){
20454             cfg.cn.push(hidden);
20455         }
20456         
20457         return cfg;
20458         
20459     },
20460     
20461     /**
20462      * return the real input element.
20463      */
20464     inputEl: function ()
20465     {
20466         return this.el.select('input.roo-' + this.inputType,true).first();
20467     },
20468     hiddenEl: function ()
20469     {
20470         return this.el.select('input.roo-hidden-value',true).first();
20471     },
20472     
20473     labelEl: function()
20474     {
20475         return this.el.select('label.control-label',true).first();
20476     },
20477     /* depricated... */
20478     
20479     label: function()
20480     {
20481         return this.labelEl();
20482     },
20483     
20484     boxLabelEl: function()
20485     {
20486         return this.el.select('label.box-label',true).first();
20487     },
20488     
20489     initEvents : function()
20490     {
20491 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20492         
20493         this.inputEl().on('click', this.onClick,  this);
20494         
20495         if (this.boxLabel) { 
20496             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20497         }
20498         
20499         this.startValue = this.getValue();
20500         
20501         if(this.groupId){
20502             Roo.bootstrap.CheckBox.register(this);
20503         }
20504     },
20505     
20506     onClick : function(e)
20507     {   
20508         if(this.fireEvent('click', this, e) !== false){
20509             this.setChecked(!this.checked);
20510         }
20511         
20512     },
20513     
20514     setChecked : function(state,suppressEvent)
20515     {
20516         this.startValue = this.getValue();
20517
20518         if(this.inputType == 'radio'){
20519             
20520             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20521                 e.dom.checked = false;
20522             });
20523             
20524             this.inputEl().dom.checked = true;
20525             
20526             this.inputEl().dom.value = this.inputValue;
20527             
20528             if(suppressEvent !== true){
20529                 this.fireEvent('check', this, true);
20530             }
20531             
20532             this.validate();
20533             
20534             return;
20535         }
20536         
20537         this.checked = state;
20538         
20539         this.inputEl().dom.checked = state;
20540         
20541         
20542         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20543         
20544         if(suppressEvent !== true){
20545             this.fireEvent('check', this, state);
20546         }
20547         
20548         this.validate();
20549     },
20550     
20551     getValue : function()
20552     {
20553         if(this.inputType == 'radio'){
20554             return this.getGroupValue();
20555         }
20556         
20557         return this.hiddenEl().dom.value;
20558         
20559     },
20560     
20561     getGroupValue : function()
20562     {
20563         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20564             return '';
20565         }
20566         
20567         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20568     },
20569     
20570     setValue : function(v,suppressEvent)
20571     {
20572         if(this.inputType == 'radio'){
20573             this.setGroupValue(v, suppressEvent);
20574             return;
20575         }
20576         
20577         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20578         
20579         this.validate();
20580     },
20581     
20582     setGroupValue : function(v, suppressEvent)
20583     {
20584         this.startValue = this.getValue();
20585         
20586         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20587             e.dom.checked = false;
20588             
20589             if(e.dom.value == v){
20590                 e.dom.checked = true;
20591             }
20592         });
20593         
20594         if(suppressEvent !== true){
20595             this.fireEvent('check', this, true);
20596         }
20597
20598         this.validate();
20599         
20600         return;
20601     },
20602     
20603     validate : function()
20604     {
20605         if(
20606                 this.disabled || 
20607                 (this.inputType == 'radio' && this.validateRadio()) ||
20608                 (this.inputType == 'checkbox' && this.validateCheckbox())
20609         ){
20610             this.markValid();
20611             return true;
20612         }
20613         
20614         this.markInvalid();
20615         return false;
20616     },
20617     
20618     validateRadio : function()
20619     {
20620         if(this.allowBlank){
20621             return true;
20622         }
20623         
20624         var valid = false;
20625         
20626         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20627             if(!e.dom.checked){
20628                 return;
20629             }
20630             
20631             valid = true;
20632             
20633             return false;
20634         });
20635         
20636         return valid;
20637     },
20638     
20639     validateCheckbox : function()
20640     {
20641         if(!this.groupId){
20642             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20643             //return (this.getValue() == this.inputValue) ? true : false;
20644         }
20645         
20646         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20647         
20648         if(!group){
20649             return false;
20650         }
20651         
20652         var r = false;
20653         
20654         for(var i in group){
20655             if(group[i].el.isVisible(true)){
20656                 r = false;
20657                 break;
20658             }
20659             
20660             r = true;
20661         }
20662         
20663         for(var i in group){
20664             if(r){
20665                 break;
20666             }
20667             
20668             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20669         }
20670         
20671         return r;
20672     },
20673     
20674     /**
20675      * Mark this field as valid
20676      */
20677     markValid : function()
20678     {
20679         var _this = this;
20680         
20681         this.fireEvent('valid', this);
20682         
20683         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20684         
20685         if(this.groupId){
20686             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20687         }
20688         
20689         if(label){
20690             label.markValid();
20691         }
20692
20693         if(this.inputType == 'radio'){
20694             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20695                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20696                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20697             });
20698             
20699             return;
20700         }
20701
20702         if(!this.groupId){
20703             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20704             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20705             return;
20706         }
20707         
20708         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20709         
20710         if(!group){
20711             return;
20712         }
20713         
20714         for(var i in group){
20715             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20716             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20717         }
20718     },
20719     
20720      /**
20721      * Mark this field as invalid
20722      * @param {String} msg The validation message
20723      */
20724     markInvalid : function(msg)
20725     {
20726         if(this.allowBlank){
20727             return;
20728         }
20729         
20730         var _this = this;
20731         
20732         this.fireEvent('invalid', this, msg);
20733         
20734         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20735         
20736         if(this.groupId){
20737             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20738         }
20739         
20740         if(label){
20741             label.markInvalid();
20742         }
20743             
20744         if(this.inputType == 'radio'){
20745             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20746                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20747                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20748             });
20749             
20750             return;
20751         }
20752         
20753         if(!this.groupId){
20754             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20755             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20756             return;
20757         }
20758         
20759         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20760         
20761         if(!group){
20762             return;
20763         }
20764         
20765         for(var i in group){
20766             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20767             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20768         }
20769         
20770     },
20771     
20772     clearInvalid : function()
20773     {
20774         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20775         
20776         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20777         
20778         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20779         
20780         if (label && label.iconEl) {
20781             label.iconEl.removeClass(label.validClass);
20782             label.iconEl.removeClass(label.invalidClass);
20783         }
20784     },
20785     
20786     disable : function()
20787     {
20788         if(this.inputType != 'radio'){
20789             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20790             return;
20791         }
20792         
20793         var _this = this;
20794         
20795         if(this.rendered){
20796             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20797                 _this.getActionEl().addClass(this.disabledClass);
20798                 e.dom.disabled = true;
20799             });
20800         }
20801         
20802         this.disabled = true;
20803         this.fireEvent("disable", this);
20804         return this;
20805     },
20806
20807     enable : function()
20808     {
20809         if(this.inputType != 'radio'){
20810             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20811             return;
20812         }
20813         
20814         var _this = this;
20815         
20816         if(this.rendered){
20817             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20818                 _this.getActionEl().removeClass(this.disabledClass);
20819                 e.dom.disabled = false;
20820             });
20821         }
20822         
20823         this.disabled = false;
20824         this.fireEvent("enable", this);
20825         return this;
20826     },
20827     
20828     setBoxLabel : function(v)
20829     {
20830         this.boxLabel = v;
20831         
20832         if(this.rendered){
20833             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20834         }
20835     }
20836
20837 });
20838
20839 Roo.apply(Roo.bootstrap.CheckBox, {
20840     
20841     groups: {},
20842     
20843      /**
20844     * register a CheckBox Group
20845     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20846     */
20847     register : function(checkbox)
20848     {
20849         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20850             this.groups[checkbox.groupId] = {};
20851         }
20852         
20853         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20854             return;
20855         }
20856         
20857         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20858         
20859     },
20860     /**
20861     * fetch a CheckBox Group based on the group ID
20862     * @param {string} the group ID
20863     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20864     */
20865     get: function(groupId) {
20866         if (typeof(this.groups[groupId]) == 'undefined') {
20867             return false;
20868         }
20869         
20870         return this.groups[groupId] ;
20871     }
20872     
20873     
20874 });
20875 /*
20876  * - LGPL
20877  *
20878  * RadioItem
20879  * 
20880  */
20881
20882 /**
20883  * @class Roo.bootstrap.Radio
20884  * @extends Roo.bootstrap.Component
20885  * Bootstrap Radio class
20886  * @cfg {String} boxLabel - the label associated
20887  * @cfg {String} value - the value of radio
20888  * 
20889  * @constructor
20890  * Create a new Radio
20891  * @param {Object} config The config object
20892  */
20893 Roo.bootstrap.Radio = function(config){
20894     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20895     
20896 };
20897
20898 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20899     
20900     boxLabel : '',
20901     
20902     value : '',
20903     
20904     getAutoCreate : function()
20905     {
20906         var cfg = {
20907             tag : 'div',
20908             cls : 'form-group radio',
20909             cn : [
20910                 {
20911                     tag : 'label',
20912                     cls : 'box-label',
20913                     html : this.boxLabel
20914                 }
20915             ]
20916         };
20917         
20918         return cfg;
20919     },
20920     
20921     initEvents : function() 
20922     {
20923         this.parent().register(this);
20924         
20925         this.el.on('click', this.onClick, this);
20926         
20927     },
20928     
20929     onClick : function()
20930     {
20931         this.setChecked(true);
20932     },
20933     
20934     setChecked : function(state, suppressEvent)
20935     {
20936         this.parent().setValue(this.value, suppressEvent);
20937         
20938     },
20939     
20940     setBoxLabel : function(v)
20941     {
20942         this.boxLabel = v;
20943         
20944         if(this.rendered){
20945             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20946         }
20947     }
20948     
20949 });
20950  
20951
20952  /*
20953  * - LGPL
20954  *
20955  * Input
20956  * 
20957  */
20958
20959 /**
20960  * @class Roo.bootstrap.SecurePass
20961  * @extends Roo.bootstrap.Input
20962  * Bootstrap SecurePass class
20963  *
20964  * 
20965  * @constructor
20966  * Create a new SecurePass
20967  * @param {Object} config The config object
20968  */
20969  
20970 Roo.bootstrap.SecurePass = function (config) {
20971     // these go here, so the translation tool can replace them..
20972     this.errors = {
20973         PwdEmpty: "Please type a password, and then retype it to confirm.",
20974         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20975         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20976         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20977         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20978         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20979         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20980         TooWeak: "Your password is Too Weak."
20981     },
20982     this.meterLabel = "Password strength:";
20983     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20984     this.meterClass = [
20985         "roo-password-meter-tooweak", 
20986         "roo-password-meter-weak", 
20987         "roo-password-meter-medium", 
20988         "roo-password-meter-strong", 
20989         "roo-password-meter-grey"
20990     ];
20991     
20992     this.errors = {};
20993     
20994     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20995 }
20996
20997 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20998     /**
20999      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21000      * {
21001      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21002      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21003      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21004      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21005      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21006      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21007      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21008      * })
21009      */
21010     // private
21011     
21012     meterWidth: 300,
21013     errorMsg :'',    
21014     errors: false,
21015     imageRoot: '/',
21016     /**
21017      * @cfg {String/Object} Label for the strength meter (defaults to
21018      * 'Password strength:')
21019      */
21020     // private
21021     meterLabel: '',
21022     /**
21023      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21024      * ['Weak', 'Medium', 'Strong'])
21025      */
21026     // private    
21027     pwdStrengths: false,    
21028     // private
21029     strength: 0,
21030     // private
21031     _lastPwd: null,
21032     // private
21033     kCapitalLetter: 0,
21034     kSmallLetter: 1,
21035     kDigit: 2,
21036     kPunctuation: 3,
21037     
21038     insecure: false,
21039     // private
21040     initEvents: function ()
21041     {
21042         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21043
21044         if (this.el.is('input[type=password]') && Roo.isSafari) {
21045             this.el.on('keydown', this.SafariOnKeyDown, this);
21046         }
21047
21048         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21049     },
21050     // private
21051     onRender: function (ct, position)
21052     {
21053         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21054         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21055         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21056
21057         this.trigger.createChild({
21058                    cn: [
21059                     {
21060                     //id: 'PwdMeter',
21061                     tag: 'div',
21062                     cls: 'roo-password-meter-grey col-xs-12',
21063                     style: {
21064                         //width: 0,
21065                         //width: this.meterWidth + 'px'                                                
21066                         }
21067                     },
21068                     {                            
21069                          cls: 'roo-password-meter-text'                          
21070                     }
21071                 ]            
21072         });
21073
21074          
21075         if (this.hideTrigger) {
21076             this.trigger.setDisplayed(false);
21077         }
21078         this.setSize(this.width || '', this.height || '');
21079     },
21080     // private
21081     onDestroy: function ()
21082     {
21083         if (this.trigger) {
21084             this.trigger.removeAllListeners();
21085             this.trigger.remove();
21086         }
21087         if (this.wrap) {
21088             this.wrap.remove();
21089         }
21090         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21091     },
21092     // private
21093     checkStrength: function ()
21094     {
21095         var pwd = this.inputEl().getValue();
21096         if (pwd == this._lastPwd) {
21097             return;
21098         }
21099
21100         var strength;
21101         if (this.ClientSideStrongPassword(pwd)) {
21102             strength = 3;
21103         } else if (this.ClientSideMediumPassword(pwd)) {
21104             strength = 2;
21105         } else if (this.ClientSideWeakPassword(pwd)) {
21106             strength = 1;
21107         } else {
21108             strength = 0;
21109         }
21110         
21111         Roo.log('strength1: ' + strength);
21112         
21113         //var pm = this.trigger.child('div/div/div').dom;
21114         var pm = this.trigger.child('div/div');
21115         pm.removeClass(this.meterClass);
21116         pm.addClass(this.meterClass[strength]);
21117                 
21118         
21119         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21120                 
21121         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21122         
21123         this._lastPwd = pwd;
21124     },
21125     reset: function ()
21126     {
21127         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21128         
21129         this._lastPwd = '';
21130         
21131         var pm = this.trigger.child('div/div');
21132         pm.removeClass(this.meterClass);
21133         pm.addClass('roo-password-meter-grey');        
21134         
21135         
21136         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21137         
21138         pt.innerHTML = '';
21139         this.inputEl().dom.type='password';
21140     },
21141     // private
21142     validateValue: function (value)
21143     {
21144         
21145         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21146             return false;
21147         }
21148         if (value.length == 0) {
21149             if (this.allowBlank) {
21150                 this.clearInvalid();
21151                 return true;
21152             }
21153
21154             this.markInvalid(this.errors.PwdEmpty);
21155             this.errorMsg = this.errors.PwdEmpty;
21156             return false;
21157         }
21158         
21159         if(this.insecure){
21160             return true;
21161         }
21162         
21163         if ('[\x21-\x7e]*'.match(value)) {
21164             this.markInvalid(this.errors.PwdBadChar);
21165             this.errorMsg = this.errors.PwdBadChar;
21166             return false;
21167         }
21168         if (value.length < 6) {
21169             this.markInvalid(this.errors.PwdShort);
21170             this.errorMsg = this.errors.PwdShort;
21171             return false;
21172         }
21173         if (value.length > 16) {
21174             this.markInvalid(this.errors.PwdLong);
21175             this.errorMsg = this.errors.PwdLong;
21176             return false;
21177         }
21178         var strength;
21179         if (this.ClientSideStrongPassword(value)) {
21180             strength = 3;
21181         } else if (this.ClientSideMediumPassword(value)) {
21182             strength = 2;
21183         } else if (this.ClientSideWeakPassword(value)) {
21184             strength = 1;
21185         } else {
21186             strength = 0;
21187         }
21188
21189         
21190         if (strength < 2) {
21191             //this.markInvalid(this.errors.TooWeak);
21192             this.errorMsg = this.errors.TooWeak;
21193             //return false;
21194         }
21195         
21196         
21197         console.log('strength2: ' + strength);
21198         
21199         //var pm = this.trigger.child('div/div/div').dom;
21200         
21201         var pm = this.trigger.child('div/div');
21202         pm.removeClass(this.meterClass);
21203         pm.addClass(this.meterClass[strength]);
21204                 
21205         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21206                 
21207         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21208         
21209         this.errorMsg = ''; 
21210         return true;
21211     },
21212     // private
21213     CharacterSetChecks: function (type)
21214     {
21215         this.type = type;
21216         this.fResult = false;
21217     },
21218     // private
21219     isctype: function (character, type)
21220     {
21221         switch (type) {  
21222             case this.kCapitalLetter:
21223                 if (character >= 'A' && character <= 'Z') {
21224                     return true;
21225                 }
21226                 break;
21227             
21228             case this.kSmallLetter:
21229                 if (character >= 'a' && character <= 'z') {
21230                     return true;
21231                 }
21232                 break;
21233             
21234             case this.kDigit:
21235                 if (character >= '0' && character <= '9') {
21236                     return true;
21237                 }
21238                 break;
21239             
21240             case this.kPunctuation:
21241                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21242                     return true;
21243                 }
21244                 break;
21245             
21246             default:
21247                 return false;
21248         }
21249
21250     },
21251     // private
21252     IsLongEnough: function (pwd, size)
21253     {
21254         return !(pwd == null || isNaN(size) || pwd.length < size);
21255     },
21256     // private
21257     SpansEnoughCharacterSets: function (word, nb)
21258     {
21259         if (!this.IsLongEnough(word, nb))
21260         {
21261             return false;
21262         }
21263
21264         var characterSetChecks = new Array(
21265             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21266             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21267         );
21268         
21269         for (var index = 0; index < word.length; ++index) {
21270             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21271                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21272                     characterSetChecks[nCharSet].fResult = true;
21273                     break;
21274                 }
21275             }
21276         }
21277
21278         var nCharSets = 0;
21279         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21280             if (characterSetChecks[nCharSet].fResult) {
21281                 ++nCharSets;
21282             }
21283         }
21284
21285         if (nCharSets < nb) {
21286             return false;
21287         }
21288         return true;
21289     },
21290     // private
21291     ClientSideStrongPassword: function (pwd)
21292     {
21293         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21294     },
21295     // private
21296     ClientSideMediumPassword: function (pwd)
21297     {
21298         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21299     },
21300     // private
21301     ClientSideWeakPassword: function (pwd)
21302     {
21303         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21304     }
21305           
21306 })//<script type="text/javascript">
21307
21308 /*
21309  * Based  Ext JS Library 1.1.1
21310  * Copyright(c) 2006-2007, Ext JS, LLC.
21311  * LGPL
21312  *
21313  */
21314  
21315 /**
21316  * @class Roo.HtmlEditorCore
21317  * @extends Roo.Component
21318  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21319  *
21320  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21321  */
21322
21323 Roo.HtmlEditorCore = function(config){
21324     
21325     
21326     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21327     
21328     
21329     this.addEvents({
21330         /**
21331          * @event initialize
21332          * Fires when the editor is fully initialized (including the iframe)
21333          * @param {Roo.HtmlEditorCore} this
21334          */
21335         initialize: true,
21336         /**
21337          * @event activate
21338          * Fires when the editor is first receives the focus. Any insertion must wait
21339          * until after this event.
21340          * @param {Roo.HtmlEditorCore} this
21341          */
21342         activate: true,
21343          /**
21344          * @event beforesync
21345          * Fires before the textarea is updated with content from the editor iframe. Return false
21346          * to cancel the sync.
21347          * @param {Roo.HtmlEditorCore} this
21348          * @param {String} html
21349          */
21350         beforesync: true,
21351          /**
21352          * @event beforepush
21353          * Fires before the iframe editor is updated with content from the textarea. Return false
21354          * to cancel the push.
21355          * @param {Roo.HtmlEditorCore} this
21356          * @param {String} html
21357          */
21358         beforepush: true,
21359          /**
21360          * @event sync
21361          * Fires when the textarea is updated with content from the editor iframe.
21362          * @param {Roo.HtmlEditorCore} this
21363          * @param {String} html
21364          */
21365         sync: true,
21366          /**
21367          * @event push
21368          * Fires when the iframe editor is updated with content from the textarea.
21369          * @param {Roo.HtmlEditorCore} this
21370          * @param {String} html
21371          */
21372         push: true,
21373         
21374         /**
21375          * @event editorevent
21376          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21377          * @param {Roo.HtmlEditorCore} this
21378          */
21379         editorevent: true
21380         
21381     });
21382     
21383     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21384     
21385     // defaults : white / black...
21386     this.applyBlacklists();
21387     
21388     
21389     
21390 };
21391
21392
21393 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21394
21395
21396      /**
21397      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21398      */
21399     
21400     owner : false,
21401     
21402      /**
21403      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21404      *                        Roo.resizable.
21405      */
21406     resizable : false,
21407      /**
21408      * @cfg {Number} height (in pixels)
21409      */   
21410     height: 300,
21411    /**
21412      * @cfg {Number} width (in pixels)
21413      */   
21414     width: 500,
21415     
21416     /**
21417      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21418      * 
21419      */
21420     stylesheets: false,
21421     
21422     // id of frame..
21423     frameId: false,
21424     
21425     // private properties
21426     validationEvent : false,
21427     deferHeight: true,
21428     initialized : false,
21429     activated : false,
21430     sourceEditMode : false,
21431     onFocus : Roo.emptyFn,
21432     iframePad:3,
21433     hideMode:'offsets',
21434     
21435     clearUp: true,
21436     
21437     // blacklist + whitelisted elements..
21438     black: false,
21439     white: false,
21440      
21441     bodyCls : '',
21442
21443     /**
21444      * Protected method that will not generally be called directly. It
21445      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21446      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21447      */
21448     getDocMarkup : function(){
21449         // body styles..
21450         var st = '';
21451         
21452         // inherit styels from page...?? 
21453         if (this.stylesheets === false) {
21454             
21455             Roo.get(document.head).select('style').each(function(node) {
21456                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21457             });
21458             
21459             Roo.get(document.head).select('link').each(function(node) { 
21460                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21461             });
21462             
21463         } else if (!this.stylesheets.length) {
21464                 // simple..
21465                 st = '<style type="text/css">' +
21466                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21467                    '</style>';
21468         } else { 
21469             st = '<style type="text/css">' +
21470                     this.stylesheets +
21471                 '</style>';
21472         }
21473         
21474         st +=  '<style type="text/css">' +
21475             'IMG { cursor: pointer } ' +
21476         '</style>';
21477
21478         var cls = 'roo-htmleditor-body';
21479         
21480         if(this.bodyCls.length){
21481             cls += ' ' + this.bodyCls;
21482         }
21483         
21484         return '<html><head>' + st  +
21485             //<style type="text/css">' +
21486             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21487             //'</style>' +
21488             ' </head><body class="' +  cls + '"></body></html>';
21489     },
21490
21491     // private
21492     onRender : function(ct, position)
21493     {
21494         var _t = this;
21495         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21496         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21497         
21498         
21499         this.el.dom.style.border = '0 none';
21500         this.el.dom.setAttribute('tabIndex', -1);
21501         this.el.addClass('x-hidden hide');
21502         
21503         
21504         
21505         if(Roo.isIE){ // fix IE 1px bogus margin
21506             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21507         }
21508        
21509         
21510         this.frameId = Roo.id();
21511         
21512          
21513         
21514         var iframe = this.owner.wrap.createChild({
21515             tag: 'iframe',
21516             cls: 'form-control', // bootstrap..
21517             id: this.frameId,
21518             name: this.frameId,
21519             frameBorder : 'no',
21520             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21521         }, this.el
21522         );
21523         
21524         
21525         this.iframe = iframe.dom;
21526
21527          this.assignDocWin();
21528         
21529         this.doc.designMode = 'on';
21530        
21531         this.doc.open();
21532         this.doc.write(this.getDocMarkup());
21533         this.doc.close();
21534
21535         
21536         var task = { // must defer to wait for browser to be ready
21537             run : function(){
21538                 //console.log("run task?" + this.doc.readyState);
21539                 this.assignDocWin();
21540                 if(this.doc.body || this.doc.readyState == 'complete'){
21541                     try {
21542                         this.doc.designMode="on";
21543                     } catch (e) {
21544                         return;
21545                     }
21546                     Roo.TaskMgr.stop(task);
21547                     this.initEditor.defer(10, this);
21548                 }
21549             },
21550             interval : 10,
21551             duration: 10000,
21552             scope: this
21553         };
21554         Roo.TaskMgr.start(task);
21555
21556     },
21557
21558     // private
21559     onResize : function(w, h)
21560     {
21561          Roo.log('resize: ' +w + ',' + h );
21562         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21563         if(!this.iframe){
21564             return;
21565         }
21566         if(typeof w == 'number'){
21567             
21568             this.iframe.style.width = w + 'px';
21569         }
21570         if(typeof h == 'number'){
21571             
21572             this.iframe.style.height = h + 'px';
21573             if(this.doc){
21574                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21575             }
21576         }
21577         
21578     },
21579
21580     /**
21581      * Toggles the editor between standard and source edit mode.
21582      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21583      */
21584     toggleSourceEdit : function(sourceEditMode){
21585         
21586         this.sourceEditMode = sourceEditMode === true;
21587         
21588         if(this.sourceEditMode){
21589  
21590             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21591             
21592         }else{
21593             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21594             //this.iframe.className = '';
21595             this.deferFocus();
21596         }
21597         //this.setSize(this.owner.wrap.getSize());
21598         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21599     },
21600
21601     
21602   
21603
21604     /**
21605      * Protected method that will not generally be called directly. If you need/want
21606      * custom HTML cleanup, this is the method you should override.
21607      * @param {String} html The HTML to be cleaned
21608      * return {String} The cleaned HTML
21609      */
21610     cleanHtml : function(html){
21611         html = String(html);
21612         if(html.length > 5){
21613             if(Roo.isSafari){ // strip safari nonsense
21614                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21615             }
21616         }
21617         if(html == '&nbsp;'){
21618             html = '';
21619         }
21620         return html;
21621     },
21622
21623     /**
21624      * HTML Editor -> Textarea
21625      * Protected method that will not generally be called directly. Syncs the contents
21626      * of the editor iframe with the textarea.
21627      */
21628     syncValue : function(){
21629         if(this.initialized){
21630             var bd = (this.doc.body || this.doc.documentElement);
21631             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21632             var html = bd.innerHTML;
21633             if(Roo.isSafari){
21634                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21635                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21636                 if(m && m[1]){
21637                     html = '<div style="'+m[0]+'">' + html + '</div>';
21638                 }
21639             }
21640             html = this.cleanHtml(html);
21641             // fix up the special chars.. normaly like back quotes in word...
21642             // however we do not want to do this with chinese..
21643             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21644                 var cc = b.charCodeAt();
21645                 if (
21646                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21647                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21648                     (cc >= 0xf900 && cc < 0xfb00 )
21649                 ) {
21650                         return b;
21651                 }
21652                 return "&#"+cc+";" 
21653             });
21654             if(this.owner.fireEvent('beforesync', this, html) !== false){
21655                 this.el.dom.value = html;
21656                 this.owner.fireEvent('sync', this, html);
21657             }
21658         }
21659     },
21660
21661     /**
21662      * Protected method that will not generally be called directly. Pushes the value of the textarea
21663      * into the iframe editor.
21664      */
21665     pushValue : function(){
21666         if(this.initialized){
21667             var v = this.el.dom.value.trim();
21668             
21669 //            if(v.length < 1){
21670 //                v = '&#160;';
21671 //            }
21672             
21673             if(this.owner.fireEvent('beforepush', this, v) !== false){
21674                 var d = (this.doc.body || this.doc.documentElement);
21675                 d.innerHTML = v;
21676                 this.cleanUpPaste();
21677                 this.el.dom.value = d.innerHTML;
21678                 this.owner.fireEvent('push', this, v);
21679             }
21680         }
21681     },
21682
21683     // private
21684     deferFocus : function(){
21685         this.focus.defer(10, this);
21686     },
21687
21688     // doc'ed in Field
21689     focus : function(){
21690         if(this.win && !this.sourceEditMode){
21691             this.win.focus();
21692         }else{
21693             this.el.focus();
21694         }
21695     },
21696     
21697     assignDocWin: function()
21698     {
21699         var iframe = this.iframe;
21700         
21701          if(Roo.isIE){
21702             this.doc = iframe.contentWindow.document;
21703             this.win = iframe.contentWindow;
21704         } else {
21705 //            if (!Roo.get(this.frameId)) {
21706 //                return;
21707 //            }
21708 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21709 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21710             
21711             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21712                 return;
21713             }
21714             
21715             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21716             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21717         }
21718     },
21719     
21720     // private
21721     initEditor : function(){
21722         //console.log("INIT EDITOR");
21723         this.assignDocWin();
21724         
21725         
21726         
21727         this.doc.designMode="on";
21728         this.doc.open();
21729         this.doc.write(this.getDocMarkup());
21730         this.doc.close();
21731         
21732         var dbody = (this.doc.body || this.doc.documentElement);
21733         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21734         // this copies styles from the containing element into thsi one..
21735         // not sure why we need all of this..
21736         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21737         
21738         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21739         //ss['background-attachment'] = 'fixed'; // w3c
21740         dbody.bgProperties = 'fixed'; // ie
21741         //Roo.DomHelper.applyStyles(dbody, ss);
21742         Roo.EventManager.on(this.doc, {
21743             //'mousedown': this.onEditorEvent,
21744             'mouseup': this.onEditorEvent,
21745             'dblclick': this.onEditorEvent,
21746             'click': this.onEditorEvent,
21747             'keyup': this.onEditorEvent,
21748             buffer:100,
21749             scope: this
21750         });
21751         if(Roo.isGecko){
21752             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21753         }
21754         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21755             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21756         }
21757         this.initialized = true;
21758
21759         this.owner.fireEvent('initialize', this);
21760         this.pushValue();
21761     },
21762
21763     // private
21764     onDestroy : function(){
21765         
21766         
21767         
21768         if(this.rendered){
21769             
21770             //for (var i =0; i < this.toolbars.length;i++) {
21771             //    // fixme - ask toolbars for heights?
21772             //    this.toolbars[i].onDestroy();
21773            // }
21774             
21775             //this.wrap.dom.innerHTML = '';
21776             //this.wrap.remove();
21777         }
21778     },
21779
21780     // private
21781     onFirstFocus : function(){
21782         
21783         this.assignDocWin();
21784         
21785         
21786         this.activated = true;
21787          
21788     
21789         if(Roo.isGecko){ // prevent silly gecko errors
21790             this.win.focus();
21791             var s = this.win.getSelection();
21792             if(!s.focusNode || s.focusNode.nodeType != 3){
21793                 var r = s.getRangeAt(0);
21794                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21795                 r.collapse(true);
21796                 this.deferFocus();
21797             }
21798             try{
21799                 this.execCmd('useCSS', true);
21800                 this.execCmd('styleWithCSS', false);
21801             }catch(e){}
21802         }
21803         this.owner.fireEvent('activate', this);
21804     },
21805
21806     // private
21807     adjustFont: function(btn){
21808         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21809         //if(Roo.isSafari){ // safari
21810         //    adjust *= 2;
21811        // }
21812         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21813         if(Roo.isSafari){ // safari
21814             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21815             v =  (v < 10) ? 10 : v;
21816             v =  (v > 48) ? 48 : v;
21817             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21818             
21819         }
21820         
21821         
21822         v = Math.max(1, v+adjust);
21823         
21824         this.execCmd('FontSize', v  );
21825     },
21826
21827     onEditorEvent : function(e)
21828     {
21829         this.owner.fireEvent('editorevent', this, e);
21830       //  this.updateToolbar();
21831         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21832     },
21833
21834     insertTag : function(tg)
21835     {
21836         // could be a bit smarter... -> wrap the current selected tRoo..
21837         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21838             
21839             range = this.createRange(this.getSelection());
21840             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21841             wrappingNode.appendChild(range.extractContents());
21842             range.insertNode(wrappingNode);
21843
21844             return;
21845             
21846             
21847             
21848         }
21849         this.execCmd("formatblock",   tg);
21850         
21851     },
21852     
21853     insertText : function(txt)
21854     {
21855         
21856         
21857         var range = this.createRange();
21858         range.deleteContents();
21859                //alert(Sender.getAttribute('label'));
21860                
21861         range.insertNode(this.doc.createTextNode(txt));
21862     } ,
21863     
21864      
21865
21866     /**
21867      * Executes a Midas editor command on the editor document and performs necessary focus and
21868      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21869      * @param {String} cmd The Midas command
21870      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21871      */
21872     relayCmd : function(cmd, value){
21873         this.win.focus();
21874         this.execCmd(cmd, value);
21875         this.owner.fireEvent('editorevent', this);
21876         //this.updateToolbar();
21877         this.owner.deferFocus();
21878     },
21879
21880     /**
21881      * Executes a Midas editor command directly on the editor document.
21882      * For visual commands, you should use {@link #relayCmd} instead.
21883      * <b>This should only be called after the editor is initialized.</b>
21884      * @param {String} cmd The Midas command
21885      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21886      */
21887     execCmd : function(cmd, value){
21888         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21889         this.syncValue();
21890     },
21891  
21892  
21893    
21894     /**
21895      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21896      * to insert tRoo.
21897      * @param {String} text | dom node.. 
21898      */
21899     insertAtCursor : function(text)
21900     {
21901         
21902         if(!this.activated){
21903             return;
21904         }
21905         /*
21906         if(Roo.isIE){
21907             this.win.focus();
21908             var r = this.doc.selection.createRange();
21909             if(r){
21910                 r.collapse(true);
21911                 r.pasteHTML(text);
21912                 this.syncValue();
21913                 this.deferFocus();
21914             
21915             }
21916             return;
21917         }
21918         */
21919         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21920             this.win.focus();
21921             
21922             
21923             // from jquery ui (MIT licenced)
21924             var range, node;
21925             var win = this.win;
21926             
21927             if (win.getSelection && win.getSelection().getRangeAt) {
21928                 range = win.getSelection().getRangeAt(0);
21929                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21930                 range.insertNode(node);
21931             } else if (win.document.selection && win.document.selection.createRange) {
21932                 // no firefox support
21933                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21934                 win.document.selection.createRange().pasteHTML(txt);
21935             } else {
21936                 // no firefox support
21937                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21938                 this.execCmd('InsertHTML', txt);
21939             } 
21940             
21941             this.syncValue();
21942             
21943             this.deferFocus();
21944         }
21945     },
21946  // private
21947     mozKeyPress : function(e){
21948         if(e.ctrlKey){
21949             var c = e.getCharCode(), cmd;
21950           
21951             if(c > 0){
21952                 c = String.fromCharCode(c).toLowerCase();
21953                 switch(c){
21954                     case 'b':
21955                         cmd = 'bold';
21956                         break;
21957                     case 'i':
21958                         cmd = 'italic';
21959                         break;
21960                     
21961                     case 'u':
21962                         cmd = 'underline';
21963                         break;
21964                     
21965                     case 'v':
21966                         this.cleanUpPaste.defer(100, this);
21967                         return;
21968                         
21969                 }
21970                 if(cmd){
21971                     this.win.focus();
21972                     this.execCmd(cmd);
21973                     this.deferFocus();
21974                     e.preventDefault();
21975                 }
21976                 
21977             }
21978         }
21979     },
21980
21981     // private
21982     fixKeys : function(){ // load time branching for fastest keydown performance
21983         if(Roo.isIE){
21984             return function(e){
21985                 var k = e.getKey(), r;
21986                 if(k == e.TAB){
21987                     e.stopEvent();
21988                     r = this.doc.selection.createRange();
21989                     if(r){
21990                         r.collapse(true);
21991                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21992                         this.deferFocus();
21993                     }
21994                     return;
21995                 }
21996                 
21997                 if(k == e.ENTER){
21998                     r = this.doc.selection.createRange();
21999                     if(r){
22000                         var target = r.parentElement();
22001                         if(!target || target.tagName.toLowerCase() != 'li'){
22002                             e.stopEvent();
22003                             r.pasteHTML('<br />');
22004                             r.collapse(false);
22005                             r.select();
22006                         }
22007                     }
22008                 }
22009                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22010                     this.cleanUpPaste.defer(100, this);
22011                     return;
22012                 }
22013                 
22014                 
22015             };
22016         }else if(Roo.isOpera){
22017             return function(e){
22018                 var k = e.getKey();
22019                 if(k == e.TAB){
22020                     e.stopEvent();
22021                     this.win.focus();
22022                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22023                     this.deferFocus();
22024                 }
22025                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22026                     this.cleanUpPaste.defer(100, this);
22027                     return;
22028                 }
22029                 
22030             };
22031         }else if(Roo.isSafari){
22032             return function(e){
22033                 var k = e.getKey();
22034                 
22035                 if(k == e.TAB){
22036                     e.stopEvent();
22037                     this.execCmd('InsertText','\t');
22038                     this.deferFocus();
22039                     return;
22040                 }
22041                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22042                     this.cleanUpPaste.defer(100, this);
22043                     return;
22044                 }
22045                 
22046              };
22047         }
22048     }(),
22049     
22050     getAllAncestors: function()
22051     {
22052         var p = this.getSelectedNode();
22053         var a = [];
22054         if (!p) {
22055             a.push(p); // push blank onto stack..
22056             p = this.getParentElement();
22057         }
22058         
22059         
22060         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22061             a.push(p);
22062             p = p.parentNode;
22063         }
22064         a.push(this.doc.body);
22065         return a;
22066     },
22067     lastSel : false,
22068     lastSelNode : false,
22069     
22070     
22071     getSelection : function() 
22072     {
22073         this.assignDocWin();
22074         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22075     },
22076     
22077     getSelectedNode: function() 
22078     {
22079         // this may only work on Gecko!!!
22080         
22081         // should we cache this!!!!
22082         
22083         
22084         
22085          
22086         var range = this.createRange(this.getSelection()).cloneRange();
22087         
22088         if (Roo.isIE) {
22089             var parent = range.parentElement();
22090             while (true) {
22091                 var testRange = range.duplicate();
22092                 testRange.moveToElementText(parent);
22093                 if (testRange.inRange(range)) {
22094                     break;
22095                 }
22096                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22097                     break;
22098                 }
22099                 parent = parent.parentElement;
22100             }
22101             return parent;
22102         }
22103         
22104         // is ancestor a text element.
22105         var ac =  range.commonAncestorContainer;
22106         if (ac.nodeType == 3) {
22107             ac = ac.parentNode;
22108         }
22109         
22110         var ar = ac.childNodes;
22111          
22112         var nodes = [];
22113         var other_nodes = [];
22114         var has_other_nodes = false;
22115         for (var i=0;i<ar.length;i++) {
22116             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22117                 continue;
22118             }
22119             // fullly contained node.
22120             
22121             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22122                 nodes.push(ar[i]);
22123                 continue;
22124             }
22125             
22126             // probably selected..
22127             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22128                 other_nodes.push(ar[i]);
22129                 continue;
22130             }
22131             // outer..
22132             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22133                 continue;
22134             }
22135             
22136             
22137             has_other_nodes = true;
22138         }
22139         if (!nodes.length && other_nodes.length) {
22140             nodes= other_nodes;
22141         }
22142         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22143             return false;
22144         }
22145         
22146         return nodes[0];
22147     },
22148     createRange: function(sel)
22149     {
22150         // this has strange effects when using with 
22151         // top toolbar - not sure if it's a great idea.
22152         //this.editor.contentWindow.focus();
22153         if (typeof sel != "undefined") {
22154             try {
22155                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22156             } catch(e) {
22157                 return this.doc.createRange();
22158             }
22159         } else {
22160             return this.doc.createRange();
22161         }
22162     },
22163     getParentElement: function()
22164     {
22165         
22166         this.assignDocWin();
22167         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22168         
22169         var range = this.createRange(sel);
22170          
22171         try {
22172             var p = range.commonAncestorContainer;
22173             while (p.nodeType == 3) { // text node
22174                 p = p.parentNode;
22175             }
22176             return p;
22177         } catch (e) {
22178             return null;
22179         }
22180     
22181     },
22182     /***
22183      *
22184      * Range intersection.. the hard stuff...
22185      *  '-1' = before
22186      *  '0' = hits..
22187      *  '1' = after.
22188      *         [ -- selected range --- ]
22189      *   [fail]                        [fail]
22190      *
22191      *    basically..
22192      *      if end is before start or  hits it. fail.
22193      *      if start is after end or hits it fail.
22194      *
22195      *   if either hits (but other is outside. - then it's not 
22196      *   
22197      *    
22198      **/
22199     
22200     
22201     // @see http://www.thismuchiknow.co.uk/?p=64.
22202     rangeIntersectsNode : function(range, node)
22203     {
22204         var nodeRange = node.ownerDocument.createRange();
22205         try {
22206             nodeRange.selectNode(node);
22207         } catch (e) {
22208             nodeRange.selectNodeContents(node);
22209         }
22210     
22211         var rangeStartRange = range.cloneRange();
22212         rangeStartRange.collapse(true);
22213     
22214         var rangeEndRange = range.cloneRange();
22215         rangeEndRange.collapse(false);
22216     
22217         var nodeStartRange = nodeRange.cloneRange();
22218         nodeStartRange.collapse(true);
22219     
22220         var nodeEndRange = nodeRange.cloneRange();
22221         nodeEndRange.collapse(false);
22222     
22223         return rangeStartRange.compareBoundaryPoints(
22224                  Range.START_TO_START, nodeEndRange) == -1 &&
22225                rangeEndRange.compareBoundaryPoints(
22226                  Range.START_TO_START, nodeStartRange) == 1;
22227         
22228          
22229     },
22230     rangeCompareNode : function(range, node)
22231     {
22232         var nodeRange = node.ownerDocument.createRange();
22233         try {
22234             nodeRange.selectNode(node);
22235         } catch (e) {
22236             nodeRange.selectNodeContents(node);
22237         }
22238         
22239         
22240         range.collapse(true);
22241     
22242         nodeRange.collapse(true);
22243      
22244         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22245         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22246          
22247         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22248         
22249         var nodeIsBefore   =  ss == 1;
22250         var nodeIsAfter    = ee == -1;
22251         
22252         if (nodeIsBefore && nodeIsAfter) {
22253             return 0; // outer
22254         }
22255         if (!nodeIsBefore && nodeIsAfter) {
22256             return 1; //right trailed.
22257         }
22258         
22259         if (nodeIsBefore && !nodeIsAfter) {
22260             return 2;  // left trailed.
22261         }
22262         // fully contined.
22263         return 3;
22264     },
22265
22266     // private? - in a new class?
22267     cleanUpPaste :  function()
22268     {
22269         // cleans up the whole document..
22270         Roo.log('cleanuppaste');
22271         
22272         this.cleanUpChildren(this.doc.body);
22273         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22274         if (clean != this.doc.body.innerHTML) {
22275             this.doc.body.innerHTML = clean;
22276         }
22277         
22278     },
22279     
22280     cleanWordChars : function(input) {// change the chars to hex code
22281         var he = Roo.HtmlEditorCore;
22282         
22283         var output = input;
22284         Roo.each(he.swapCodes, function(sw) { 
22285             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22286             
22287             output = output.replace(swapper, sw[1]);
22288         });
22289         
22290         return output;
22291     },
22292     
22293     
22294     cleanUpChildren : function (n)
22295     {
22296         if (!n.childNodes.length) {
22297             return;
22298         }
22299         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22300            this.cleanUpChild(n.childNodes[i]);
22301         }
22302     },
22303     
22304     
22305         
22306     
22307     cleanUpChild : function (node)
22308     {
22309         var ed = this;
22310         //console.log(node);
22311         if (node.nodeName == "#text") {
22312             // clean up silly Windows -- stuff?
22313             return; 
22314         }
22315         if (node.nodeName == "#comment") {
22316             node.parentNode.removeChild(node);
22317             // clean up silly Windows -- stuff?
22318             return; 
22319         }
22320         var lcname = node.tagName.toLowerCase();
22321         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22322         // whitelist of tags..
22323         
22324         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22325             // remove node.
22326             node.parentNode.removeChild(node);
22327             return;
22328             
22329         }
22330         
22331         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22332         
22333         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22334         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22335         
22336         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22337         //    remove_keep_children = true;
22338         //}
22339         
22340         if (remove_keep_children) {
22341             this.cleanUpChildren(node);
22342             // inserts everything just before this node...
22343             while (node.childNodes.length) {
22344                 var cn = node.childNodes[0];
22345                 node.removeChild(cn);
22346                 node.parentNode.insertBefore(cn, node);
22347             }
22348             node.parentNode.removeChild(node);
22349             return;
22350         }
22351         
22352         if (!node.attributes || !node.attributes.length) {
22353             this.cleanUpChildren(node);
22354             return;
22355         }
22356         
22357         function cleanAttr(n,v)
22358         {
22359             
22360             if (v.match(/^\./) || v.match(/^\//)) {
22361                 return;
22362             }
22363             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22364                 return;
22365             }
22366             if (v.match(/^#/)) {
22367                 return;
22368             }
22369 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22370             node.removeAttribute(n);
22371             
22372         }
22373         
22374         var cwhite = this.cwhite;
22375         var cblack = this.cblack;
22376             
22377         function cleanStyle(n,v)
22378         {
22379             if (v.match(/expression/)) { //XSS?? should we even bother..
22380                 node.removeAttribute(n);
22381                 return;
22382             }
22383             
22384             var parts = v.split(/;/);
22385             var clean = [];
22386             
22387             Roo.each(parts, function(p) {
22388                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22389                 if (!p.length) {
22390                     return true;
22391                 }
22392                 var l = p.split(':').shift().replace(/\s+/g,'');
22393                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22394                 
22395                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22396 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22397                     //node.removeAttribute(n);
22398                     return true;
22399                 }
22400                 //Roo.log()
22401                 // only allow 'c whitelisted system attributes'
22402                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22403 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22404                     //node.removeAttribute(n);
22405                     return true;
22406                 }
22407                 
22408                 
22409                  
22410                 
22411                 clean.push(p);
22412                 return true;
22413             });
22414             if (clean.length) { 
22415                 node.setAttribute(n, clean.join(';'));
22416             } else {
22417                 node.removeAttribute(n);
22418             }
22419             
22420         }
22421         
22422         
22423         for (var i = node.attributes.length-1; i > -1 ; i--) {
22424             var a = node.attributes[i];
22425             //console.log(a);
22426             
22427             if (a.name.toLowerCase().substr(0,2)=='on')  {
22428                 node.removeAttribute(a.name);
22429                 continue;
22430             }
22431             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22432                 node.removeAttribute(a.name);
22433                 continue;
22434             }
22435             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22436                 cleanAttr(a.name,a.value); // fixme..
22437                 continue;
22438             }
22439             if (a.name == 'style') {
22440                 cleanStyle(a.name,a.value);
22441                 continue;
22442             }
22443             /// clean up MS crap..
22444             // tecnically this should be a list of valid class'es..
22445             
22446             
22447             if (a.name == 'class') {
22448                 if (a.value.match(/^Mso/)) {
22449                     node.className = '';
22450                 }
22451                 
22452                 if (a.value.match(/^body$/)) {
22453                     node.className = '';
22454                 }
22455                 continue;
22456             }
22457             
22458             // style cleanup!?
22459             // class cleanup?
22460             
22461         }
22462         
22463         
22464         this.cleanUpChildren(node);
22465         
22466         
22467     },
22468     
22469     /**
22470      * Clean up MS wordisms...
22471      */
22472     cleanWord : function(node)
22473     {
22474         
22475         
22476         if (!node) {
22477             this.cleanWord(this.doc.body);
22478             return;
22479         }
22480         if (node.nodeName == "#text") {
22481             // clean up silly Windows -- stuff?
22482             return; 
22483         }
22484         if (node.nodeName == "#comment") {
22485             node.parentNode.removeChild(node);
22486             // clean up silly Windows -- stuff?
22487             return; 
22488         }
22489         
22490         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22491             node.parentNode.removeChild(node);
22492             return;
22493         }
22494         
22495         // remove - but keep children..
22496         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22497             while (node.childNodes.length) {
22498                 var cn = node.childNodes[0];
22499                 node.removeChild(cn);
22500                 node.parentNode.insertBefore(cn, node);
22501             }
22502             node.parentNode.removeChild(node);
22503             this.iterateChildren(node, this.cleanWord);
22504             return;
22505         }
22506         // clean styles
22507         if (node.className.length) {
22508             
22509             var cn = node.className.split(/\W+/);
22510             var cna = [];
22511             Roo.each(cn, function(cls) {
22512                 if (cls.match(/Mso[a-zA-Z]+/)) {
22513                     return;
22514                 }
22515                 cna.push(cls);
22516             });
22517             node.className = cna.length ? cna.join(' ') : '';
22518             if (!cna.length) {
22519                 node.removeAttribute("class");
22520             }
22521         }
22522         
22523         if (node.hasAttribute("lang")) {
22524             node.removeAttribute("lang");
22525         }
22526         
22527         if (node.hasAttribute("style")) {
22528             
22529             var styles = node.getAttribute("style").split(";");
22530             var nstyle = [];
22531             Roo.each(styles, function(s) {
22532                 if (!s.match(/:/)) {
22533                     return;
22534                 }
22535                 var kv = s.split(":");
22536                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22537                     return;
22538                 }
22539                 // what ever is left... we allow.
22540                 nstyle.push(s);
22541             });
22542             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22543             if (!nstyle.length) {
22544                 node.removeAttribute('style');
22545             }
22546         }
22547         this.iterateChildren(node, this.cleanWord);
22548         
22549         
22550         
22551     },
22552     /**
22553      * iterateChildren of a Node, calling fn each time, using this as the scole..
22554      * @param {DomNode} node node to iterate children of.
22555      * @param {Function} fn method of this class to call on each item.
22556      */
22557     iterateChildren : function(node, fn)
22558     {
22559         if (!node.childNodes.length) {
22560                 return;
22561         }
22562         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22563            fn.call(this, node.childNodes[i])
22564         }
22565     },
22566     
22567     
22568     /**
22569      * cleanTableWidths.
22570      *
22571      * Quite often pasting from word etc.. results in tables with column and widths.
22572      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22573      *
22574      */
22575     cleanTableWidths : function(node)
22576     {
22577          
22578          
22579         if (!node) {
22580             this.cleanTableWidths(this.doc.body);
22581             return;
22582         }
22583         
22584         // ignore list...
22585         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22586             return; 
22587         }
22588         Roo.log(node.tagName);
22589         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22590             this.iterateChildren(node, this.cleanTableWidths);
22591             return;
22592         }
22593         if (node.hasAttribute('width')) {
22594             node.removeAttribute('width');
22595         }
22596         
22597          
22598         if (node.hasAttribute("style")) {
22599             // pretty basic...
22600             
22601             var styles = node.getAttribute("style").split(";");
22602             var nstyle = [];
22603             Roo.each(styles, function(s) {
22604                 if (!s.match(/:/)) {
22605                     return;
22606                 }
22607                 var kv = s.split(":");
22608                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22609                     return;
22610                 }
22611                 // what ever is left... we allow.
22612                 nstyle.push(s);
22613             });
22614             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22615             if (!nstyle.length) {
22616                 node.removeAttribute('style');
22617             }
22618         }
22619         
22620         this.iterateChildren(node, this.cleanTableWidths);
22621         
22622         
22623     },
22624     
22625     
22626     
22627     
22628     domToHTML : function(currentElement, depth, nopadtext) {
22629         
22630         depth = depth || 0;
22631         nopadtext = nopadtext || false;
22632     
22633         if (!currentElement) {
22634             return this.domToHTML(this.doc.body);
22635         }
22636         
22637         //Roo.log(currentElement);
22638         var j;
22639         var allText = false;
22640         var nodeName = currentElement.nodeName;
22641         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22642         
22643         if  (nodeName == '#text') {
22644             
22645             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22646         }
22647         
22648         
22649         var ret = '';
22650         if (nodeName != 'BODY') {
22651              
22652             var i = 0;
22653             // Prints the node tagName, such as <A>, <IMG>, etc
22654             if (tagName) {
22655                 var attr = [];
22656                 for(i = 0; i < currentElement.attributes.length;i++) {
22657                     // quoting?
22658                     var aname = currentElement.attributes.item(i).name;
22659                     if (!currentElement.attributes.item(i).value.length) {
22660                         continue;
22661                     }
22662                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22663                 }
22664                 
22665                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22666             } 
22667             else {
22668                 
22669                 // eack
22670             }
22671         } else {
22672             tagName = false;
22673         }
22674         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22675             return ret;
22676         }
22677         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22678             nopadtext = true;
22679         }
22680         
22681         
22682         // Traverse the tree
22683         i = 0;
22684         var currentElementChild = currentElement.childNodes.item(i);
22685         var allText = true;
22686         var innerHTML  = '';
22687         lastnode = '';
22688         while (currentElementChild) {
22689             // Formatting code (indent the tree so it looks nice on the screen)
22690             var nopad = nopadtext;
22691             if (lastnode == 'SPAN') {
22692                 nopad  = true;
22693             }
22694             // text
22695             if  (currentElementChild.nodeName == '#text') {
22696                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22697                 toadd = nopadtext ? toadd : toadd.trim();
22698                 if (!nopad && toadd.length > 80) {
22699                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22700                 }
22701                 innerHTML  += toadd;
22702                 
22703                 i++;
22704                 currentElementChild = currentElement.childNodes.item(i);
22705                 lastNode = '';
22706                 continue;
22707             }
22708             allText = false;
22709             
22710             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22711                 
22712             // Recursively traverse the tree structure of the child node
22713             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22714             lastnode = currentElementChild.nodeName;
22715             i++;
22716             currentElementChild=currentElement.childNodes.item(i);
22717         }
22718         
22719         ret += innerHTML;
22720         
22721         if (!allText) {
22722                 // The remaining code is mostly for formatting the tree
22723             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22724         }
22725         
22726         
22727         if (tagName) {
22728             ret+= "</"+tagName+">";
22729         }
22730         return ret;
22731         
22732     },
22733         
22734     applyBlacklists : function()
22735     {
22736         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22737         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22738         
22739         this.white = [];
22740         this.black = [];
22741         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22742             if (b.indexOf(tag) > -1) {
22743                 return;
22744             }
22745             this.white.push(tag);
22746             
22747         }, this);
22748         
22749         Roo.each(w, function(tag) {
22750             if (b.indexOf(tag) > -1) {
22751                 return;
22752             }
22753             if (this.white.indexOf(tag) > -1) {
22754                 return;
22755             }
22756             this.white.push(tag);
22757             
22758         }, this);
22759         
22760         
22761         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22762             if (w.indexOf(tag) > -1) {
22763                 return;
22764             }
22765             this.black.push(tag);
22766             
22767         }, this);
22768         
22769         Roo.each(b, function(tag) {
22770             if (w.indexOf(tag) > -1) {
22771                 return;
22772             }
22773             if (this.black.indexOf(tag) > -1) {
22774                 return;
22775             }
22776             this.black.push(tag);
22777             
22778         }, this);
22779         
22780         
22781         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22782         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22783         
22784         this.cwhite = [];
22785         this.cblack = [];
22786         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22787             if (b.indexOf(tag) > -1) {
22788                 return;
22789             }
22790             this.cwhite.push(tag);
22791             
22792         }, this);
22793         
22794         Roo.each(w, function(tag) {
22795             if (b.indexOf(tag) > -1) {
22796                 return;
22797             }
22798             if (this.cwhite.indexOf(tag) > -1) {
22799                 return;
22800             }
22801             this.cwhite.push(tag);
22802             
22803         }, this);
22804         
22805         
22806         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22807             if (w.indexOf(tag) > -1) {
22808                 return;
22809             }
22810             this.cblack.push(tag);
22811             
22812         }, this);
22813         
22814         Roo.each(b, function(tag) {
22815             if (w.indexOf(tag) > -1) {
22816                 return;
22817             }
22818             if (this.cblack.indexOf(tag) > -1) {
22819                 return;
22820             }
22821             this.cblack.push(tag);
22822             
22823         }, this);
22824     },
22825     
22826     setStylesheets : function(stylesheets)
22827     {
22828         if(typeof(stylesheets) == 'string'){
22829             Roo.get(this.iframe.contentDocument.head).createChild({
22830                 tag : 'link',
22831                 rel : 'stylesheet',
22832                 type : 'text/css',
22833                 href : stylesheets
22834             });
22835             
22836             return;
22837         }
22838         var _this = this;
22839      
22840         Roo.each(stylesheets, function(s) {
22841             if(!s.length){
22842                 return;
22843             }
22844             
22845             Roo.get(_this.iframe.contentDocument.head).createChild({
22846                 tag : 'link',
22847                 rel : 'stylesheet',
22848                 type : 'text/css',
22849                 href : s
22850             });
22851         });
22852
22853         
22854     },
22855     
22856     removeStylesheets : function()
22857     {
22858         var _this = this;
22859         
22860         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22861             s.remove();
22862         });
22863     },
22864     
22865     setStyle : function(style)
22866     {
22867         Roo.get(this.iframe.contentDocument.head).createChild({
22868             tag : 'style',
22869             type : 'text/css',
22870             html : style
22871         });
22872
22873         return;
22874     }
22875     
22876     // hide stuff that is not compatible
22877     /**
22878      * @event blur
22879      * @hide
22880      */
22881     /**
22882      * @event change
22883      * @hide
22884      */
22885     /**
22886      * @event focus
22887      * @hide
22888      */
22889     /**
22890      * @event specialkey
22891      * @hide
22892      */
22893     /**
22894      * @cfg {String} fieldClass @hide
22895      */
22896     /**
22897      * @cfg {String} focusClass @hide
22898      */
22899     /**
22900      * @cfg {String} autoCreate @hide
22901      */
22902     /**
22903      * @cfg {String} inputType @hide
22904      */
22905     /**
22906      * @cfg {String} invalidClass @hide
22907      */
22908     /**
22909      * @cfg {String} invalidText @hide
22910      */
22911     /**
22912      * @cfg {String} msgFx @hide
22913      */
22914     /**
22915      * @cfg {String} validateOnBlur @hide
22916      */
22917 });
22918
22919 Roo.HtmlEditorCore.white = [
22920         'area', 'br', 'img', 'input', 'hr', 'wbr',
22921         
22922        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22923        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22924        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22925        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22926        'table',   'ul',         'xmp', 
22927        
22928        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22929       'thead',   'tr', 
22930      
22931       'dir', 'menu', 'ol', 'ul', 'dl',
22932        
22933       'embed',  'object'
22934 ];
22935
22936
22937 Roo.HtmlEditorCore.black = [
22938     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22939         'applet', // 
22940         'base',   'basefont', 'bgsound', 'blink',  'body', 
22941         'frame',  'frameset', 'head',    'html',   'ilayer', 
22942         'iframe', 'layer',  'link',     'meta',    'object',   
22943         'script', 'style' ,'title',  'xml' // clean later..
22944 ];
22945 Roo.HtmlEditorCore.clean = [
22946     'script', 'style', 'title', 'xml'
22947 ];
22948 Roo.HtmlEditorCore.remove = [
22949     'font'
22950 ];
22951 // attributes..
22952
22953 Roo.HtmlEditorCore.ablack = [
22954     'on'
22955 ];
22956     
22957 Roo.HtmlEditorCore.aclean = [ 
22958     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22959 ];
22960
22961 // protocols..
22962 Roo.HtmlEditorCore.pwhite= [
22963         'http',  'https',  'mailto'
22964 ];
22965
22966 // white listed style attributes.
22967 Roo.HtmlEditorCore.cwhite= [
22968       //  'text-align', /// default is to allow most things..
22969       
22970          
22971 //        'font-size'//??
22972 ];
22973
22974 // black listed style attributes.
22975 Roo.HtmlEditorCore.cblack= [
22976       //  'font-size' -- this can be set by the project 
22977 ];
22978
22979
22980 Roo.HtmlEditorCore.swapCodes   =[ 
22981     [    8211, "--" ], 
22982     [    8212, "--" ], 
22983     [    8216,  "'" ],  
22984     [    8217, "'" ],  
22985     [    8220, '"' ],  
22986     [    8221, '"' ],  
22987     [    8226, "*" ],  
22988     [    8230, "..." ]
22989 ]; 
22990
22991     /*
22992  * - LGPL
22993  *
22994  * HtmlEditor
22995  * 
22996  */
22997
22998 /**
22999  * @class Roo.bootstrap.HtmlEditor
23000  * @extends Roo.bootstrap.TextArea
23001  * Bootstrap HtmlEditor class
23002
23003  * @constructor
23004  * Create a new HtmlEditor
23005  * @param {Object} config The config object
23006  */
23007
23008 Roo.bootstrap.HtmlEditor = function(config){
23009     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23010     if (!this.toolbars) {
23011         this.toolbars = [];
23012     }
23013     
23014     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23015     this.addEvents({
23016             /**
23017              * @event initialize
23018              * Fires when the editor is fully initialized (including the iframe)
23019              * @param {HtmlEditor} this
23020              */
23021             initialize: true,
23022             /**
23023              * @event activate
23024              * Fires when the editor is first receives the focus. Any insertion must wait
23025              * until after this event.
23026              * @param {HtmlEditor} this
23027              */
23028             activate: true,
23029              /**
23030              * @event beforesync
23031              * Fires before the textarea is updated with content from the editor iframe. Return false
23032              * to cancel the sync.
23033              * @param {HtmlEditor} this
23034              * @param {String} html
23035              */
23036             beforesync: true,
23037              /**
23038              * @event beforepush
23039              * Fires before the iframe editor is updated with content from the textarea. Return false
23040              * to cancel the push.
23041              * @param {HtmlEditor} this
23042              * @param {String} html
23043              */
23044             beforepush: true,
23045              /**
23046              * @event sync
23047              * Fires when the textarea is updated with content from the editor iframe.
23048              * @param {HtmlEditor} this
23049              * @param {String} html
23050              */
23051             sync: true,
23052              /**
23053              * @event push
23054              * Fires when the iframe editor is updated with content from the textarea.
23055              * @param {HtmlEditor} this
23056              * @param {String} html
23057              */
23058             push: true,
23059              /**
23060              * @event editmodechange
23061              * Fires when the editor switches edit modes
23062              * @param {HtmlEditor} this
23063              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23064              */
23065             editmodechange: true,
23066             /**
23067              * @event editorevent
23068              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23069              * @param {HtmlEditor} this
23070              */
23071             editorevent: true,
23072             /**
23073              * @event firstfocus
23074              * Fires when on first focus - needed by toolbars..
23075              * @param {HtmlEditor} this
23076              */
23077             firstfocus: true,
23078             /**
23079              * @event autosave
23080              * Auto save the htmlEditor value as a file into Events
23081              * @param {HtmlEditor} this
23082              */
23083             autosave: true,
23084             /**
23085              * @event savedpreview
23086              * preview the saved version of htmlEditor
23087              * @param {HtmlEditor} this
23088              */
23089             savedpreview: true
23090         });
23091 };
23092
23093
23094 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23095     
23096     
23097       /**
23098      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23099      */
23100     toolbars : false,
23101     
23102      /**
23103     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23104     */
23105     btns : [],
23106    
23107      /**
23108      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23109      *                        Roo.resizable.
23110      */
23111     resizable : false,
23112      /**
23113      * @cfg {Number} height (in pixels)
23114      */   
23115     height: 300,
23116    /**
23117      * @cfg {Number} width (in pixels)
23118      */   
23119     width: false,
23120     
23121     /**
23122      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23123      * 
23124      */
23125     stylesheets: false,
23126     
23127     // id of frame..
23128     frameId: false,
23129     
23130     // private properties
23131     validationEvent : false,
23132     deferHeight: true,
23133     initialized : false,
23134     activated : false,
23135     
23136     onFocus : Roo.emptyFn,
23137     iframePad:3,
23138     hideMode:'offsets',
23139     
23140     tbContainer : false,
23141     
23142     bodyCls : '',
23143     
23144     toolbarContainer :function() {
23145         return this.wrap.select('.x-html-editor-tb',true).first();
23146     },
23147
23148     /**
23149      * Protected method that will not generally be called directly. It
23150      * is called when the editor creates its toolbar. Override this method if you need to
23151      * add custom toolbar buttons.
23152      * @param {HtmlEditor} editor
23153      */
23154     createToolbar : function(){
23155         Roo.log('renewing');
23156         Roo.log("create toolbars");
23157         
23158         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23159         this.toolbars[0].render(this.toolbarContainer());
23160         
23161         return;
23162         
23163 //        if (!editor.toolbars || !editor.toolbars.length) {
23164 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23165 //        }
23166 //        
23167 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23168 //            editor.toolbars[i] = Roo.factory(
23169 //                    typeof(editor.toolbars[i]) == 'string' ?
23170 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23171 //                Roo.bootstrap.HtmlEditor);
23172 //            editor.toolbars[i].init(editor);
23173 //        }
23174     },
23175
23176      
23177     // private
23178     onRender : function(ct, position)
23179     {
23180        // Roo.log("Call onRender: " + this.xtype);
23181         var _t = this;
23182         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23183       
23184         this.wrap = this.inputEl().wrap({
23185             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23186         });
23187         
23188         this.editorcore.onRender(ct, position);
23189          
23190         if (this.resizable) {
23191             this.resizeEl = new Roo.Resizable(this.wrap, {
23192                 pinned : true,
23193                 wrap: true,
23194                 dynamic : true,
23195                 minHeight : this.height,
23196                 height: this.height,
23197                 handles : this.resizable,
23198                 width: this.width,
23199                 listeners : {
23200                     resize : function(r, w, h) {
23201                         _t.onResize(w,h); // -something
23202                     }
23203                 }
23204             });
23205             
23206         }
23207         this.createToolbar(this);
23208        
23209         
23210         if(!this.width && this.resizable){
23211             this.setSize(this.wrap.getSize());
23212         }
23213         if (this.resizeEl) {
23214             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23215             // should trigger onReize..
23216         }
23217         
23218     },
23219
23220     // private
23221     onResize : function(w, h)
23222     {
23223         Roo.log('resize: ' +w + ',' + h );
23224         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23225         var ew = false;
23226         var eh = false;
23227         
23228         if(this.inputEl() ){
23229             if(typeof w == 'number'){
23230                 var aw = w - this.wrap.getFrameWidth('lr');
23231                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23232                 ew = aw;
23233             }
23234             if(typeof h == 'number'){
23235                  var tbh = -11;  // fixme it needs to tool bar size!
23236                 for (var i =0; i < this.toolbars.length;i++) {
23237                     // fixme - ask toolbars for heights?
23238                     tbh += this.toolbars[i].el.getHeight();
23239                     //if (this.toolbars[i].footer) {
23240                     //    tbh += this.toolbars[i].footer.el.getHeight();
23241                     //}
23242                 }
23243               
23244                 
23245                 
23246                 
23247                 
23248                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23249                 ah -= 5; // knock a few pixes off for look..
23250                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23251                 var eh = ah;
23252             }
23253         }
23254         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23255         this.editorcore.onResize(ew,eh);
23256         
23257     },
23258
23259     /**
23260      * Toggles the editor between standard and source edit mode.
23261      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23262      */
23263     toggleSourceEdit : function(sourceEditMode)
23264     {
23265         this.editorcore.toggleSourceEdit(sourceEditMode);
23266         
23267         if(this.editorcore.sourceEditMode){
23268             Roo.log('editor - showing textarea');
23269             
23270 //            Roo.log('in');
23271 //            Roo.log(this.syncValue());
23272             this.syncValue();
23273             this.inputEl().removeClass(['hide', 'x-hidden']);
23274             this.inputEl().dom.removeAttribute('tabIndex');
23275             this.inputEl().focus();
23276         }else{
23277             Roo.log('editor - hiding textarea');
23278 //            Roo.log('out')
23279 //            Roo.log(this.pushValue()); 
23280             this.pushValue();
23281             
23282             this.inputEl().addClass(['hide', 'x-hidden']);
23283             this.inputEl().dom.setAttribute('tabIndex', -1);
23284             //this.deferFocus();
23285         }
23286          
23287         if(this.resizable){
23288             this.setSize(this.wrap.getSize());
23289         }
23290         
23291         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23292     },
23293  
23294     // private (for BoxComponent)
23295     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23296
23297     // private (for BoxComponent)
23298     getResizeEl : function(){
23299         return this.wrap;
23300     },
23301
23302     // private (for BoxComponent)
23303     getPositionEl : function(){
23304         return this.wrap;
23305     },
23306
23307     // private
23308     initEvents : function(){
23309         this.originalValue = this.getValue();
23310     },
23311
23312 //    /**
23313 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23314 //     * @method
23315 //     */
23316 //    markInvalid : Roo.emptyFn,
23317 //    /**
23318 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23319 //     * @method
23320 //     */
23321 //    clearInvalid : Roo.emptyFn,
23322
23323     setValue : function(v){
23324         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23325         this.editorcore.pushValue();
23326     },
23327
23328      
23329     // private
23330     deferFocus : function(){
23331         this.focus.defer(10, this);
23332     },
23333
23334     // doc'ed in Field
23335     focus : function(){
23336         this.editorcore.focus();
23337         
23338     },
23339       
23340
23341     // private
23342     onDestroy : function(){
23343         
23344         
23345         
23346         if(this.rendered){
23347             
23348             for (var i =0; i < this.toolbars.length;i++) {
23349                 // fixme - ask toolbars for heights?
23350                 this.toolbars[i].onDestroy();
23351             }
23352             
23353             this.wrap.dom.innerHTML = '';
23354             this.wrap.remove();
23355         }
23356     },
23357
23358     // private
23359     onFirstFocus : function(){
23360         //Roo.log("onFirstFocus");
23361         this.editorcore.onFirstFocus();
23362          for (var i =0; i < this.toolbars.length;i++) {
23363             this.toolbars[i].onFirstFocus();
23364         }
23365         
23366     },
23367     
23368     // private
23369     syncValue : function()
23370     {   
23371         this.editorcore.syncValue();
23372     },
23373     
23374     pushValue : function()
23375     {   
23376         this.editorcore.pushValue();
23377     }
23378      
23379     
23380     // hide stuff that is not compatible
23381     /**
23382      * @event blur
23383      * @hide
23384      */
23385     /**
23386      * @event change
23387      * @hide
23388      */
23389     /**
23390      * @event focus
23391      * @hide
23392      */
23393     /**
23394      * @event specialkey
23395      * @hide
23396      */
23397     /**
23398      * @cfg {String} fieldClass @hide
23399      */
23400     /**
23401      * @cfg {String} focusClass @hide
23402      */
23403     /**
23404      * @cfg {String} autoCreate @hide
23405      */
23406     /**
23407      * @cfg {String} inputType @hide
23408      */
23409     /**
23410      * @cfg {String} invalidClass @hide
23411      */
23412     /**
23413      * @cfg {String} invalidText @hide
23414      */
23415     /**
23416      * @cfg {String} msgFx @hide
23417      */
23418     /**
23419      * @cfg {String} validateOnBlur @hide
23420      */
23421 });
23422  
23423     
23424    
23425    
23426    
23427       
23428 Roo.namespace('Roo.bootstrap.htmleditor');
23429 /**
23430  * @class Roo.bootstrap.HtmlEditorToolbar1
23431  * Basic Toolbar
23432  * 
23433  * Usage:
23434  *
23435  new Roo.bootstrap.HtmlEditor({
23436     ....
23437     toolbars : [
23438         new Roo.bootstrap.HtmlEditorToolbar1({
23439             disable : { fonts: 1 , format: 1, ..., ... , ...],
23440             btns : [ .... ]
23441         })
23442     }
23443      
23444  * 
23445  * @cfg {Object} disable List of elements to disable..
23446  * @cfg {Array} btns List of additional buttons.
23447  * 
23448  * 
23449  * NEEDS Extra CSS? 
23450  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23451  */
23452  
23453 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23454 {
23455     
23456     Roo.apply(this, config);
23457     
23458     // default disabled, based on 'good practice'..
23459     this.disable = this.disable || {};
23460     Roo.applyIf(this.disable, {
23461         fontSize : true,
23462         colors : true,
23463         specialElements : true
23464     });
23465     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23466     
23467     this.editor = config.editor;
23468     this.editorcore = config.editor.editorcore;
23469     
23470     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23471     
23472     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23473     // dont call parent... till later.
23474 }
23475 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23476      
23477     bar : true,
23478     
23479     editor : false,
23480     editorcore : false,
23481     
23482     
23483     formats : [
23484         "p" ,  
23485         "h1","h2","h3","h4","h5","h6", 
23486         "pre", "code", 
23487         "abbr", "acronym", "address", "cite", "samp", "var",
23488         'div','span'
23489     ],
23490     
23491     onRender : function(ct, position)
23492     {
23493        // Roo.log("Call onRender: " + this.xtype);
23494         
23495        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23496        Roo.log(this.el);
23497        this.el.dom.style.marginBottom = '0';
23498        var _this = this;
23499        var editorcore = this.editorcore;
23500        var editor= this.editor;
23501        
23502        var children = [];
23503        var btn = function(id,cmd , toggle, handler, html){
23504        
23505             var  event = toggle ? 'toggle' : 'click';
23506        
23507             var a = {
23508                 size : 'sm',
23509                 xtype: 'Button',
23510                 xns: Roo.bootstrap,
23511                 glyphicon : id,
23512                 cmd : id || cmd,
23513                 enableToggle:toggle !== false,
23514                 html : html || '',
23515                 pressed : toggle ? false : null,
23516                 listeners : {}
23517             };
23518             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23519                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23520             };
23521             children.push(a);
23522             return a;
23523        }
23524        
23525     //    var cb_box = function...
23526         
23527         var style = {
23528                 xtype: 'Button',
23529                 size : 'sm',
23530                 xns: Roo.bootstrap,
23531                 glyphicon : 'font',
23532                 //html : 'submit'
23533                 menu : {
23534                     xtype: 'Menu',
23535                     xns: Roo.bootstrap,
23536                     items:  []
23537                 }
23538         };
23539         Roo.each(this.formats, function(f) {
23540             style.menu.items.push({
23541                 xtype :'MenuItem',
23542                 xns: Roo.bootstrap,
23543                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23544                 tagname : f,
23545                 listeners : {
23546                     click : function()
23547                     {
23548                         editorcore.insertTag(this.tagname);
23549                         editor.focus();
23550                     }
23551                 }
23552                 
23553             });
23554         });
23555         children.push(style);   
23556         
23557         btn('bold',false,true);
23558         btn('italic',false,true);
23559         btn('align-left', 'justifyleft',true);
23560         btn('align-center', 'justifycenter',true);
23561         btn('align-right' , 'justifyright',true);
23562         btn('link', false, false, function(btn) {
23563             //Roo.log("create link?");
23564             var url = prompt(this.createLinkText, this.defaultLinkValue);
23565             if(url && url != 'http:/'+'/'){
23566                 this.editorcore.relayCmd('createlink', url);
23567             }
23568         }),
23569         btn('list','insertunorderedlist',true);
23570         btn('pencil', false,true, function(btn){
23571                 Roo.log(this);
23572                 this.toggleSourceEdit(btn.pressed);
23573         });
23574         
23575         if (this.editor.btns.length > 0) {
23576             for (var i = 0; i<this.editor.btns.length; i++) {
23577                 children.push(this.editor.btns[i]);
23578             }
23579         }
23580         
23581         /*
23582         var cog = {
23583                 xtype: 'Button',
23584                 size : 'sm',
23585                 xns: Roo.bootstrap,
23586                 glyphicon : 'cog',
23587                 //html : 'submit'
23588                 menu : {
23589                     xtype: 'Menu',
23590                     xns: Roo.bootstrap,
23591                     items:  []
23592                 }
23593         };
23594         
23595         cog.menu.items.push({
23596             xtype :'MenuItem',
23597             xns: Roo.bootstrap,
23598             html : Clean styles,
23599             tagname : f,
23600             listeners : {
23601                 click : function()
23602                 {
23603                     editorcore.insertTag(this.tagname);
23604                     editor.focus();
23605                 }
23606             }
23607             
23608         });
23609        */
23610         
23611          
23612        this.xtype = 'NavSimplebar';
23613         
23614         for(var i=0;i< children.length;i++) {
23615             
23616             this.buttons.add(this.addxtypeChild(children[i]));
23617             
23618         }
23619         
23620         editor.on('editorevent', this.updateToolbar, this);
23621     },
23622     onBtnClick : function(id)
23623     {
23624        this.editorcore.relayCmd(id);
23625        this.editorcore.focus();
23626     },
23627     
23628     /**
23629      * Protected method that will not generally be called directly. It triggers
23630      * a toolbar update by reading the markup state of the current selection in the editor.
23631      */
23632     updateToolbar: function(){
23633
23634         if(!this.editorcore.activated){
23635             this.editor.onFirstFocus(); // is this neeed?
23636             return;
23637         }
23638
23639         var btns = this.buttons; 
23640         var doc = this.editorcore.doc;
23641         btns.get('bold').setActive(doc.queryCommandState('bold'));
23642         btns.get('italic').setActive(doc.queryCommandState('italic'));
23643         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23644         
23645         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23646         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23647         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23648         
23649         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23650         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23651          /*
23652         
23653         var ans = this.editorcore.getAllAncestors();
23654         if (this.formatCombo) {
23655             
23656             
23657             var store = this.formatCombo.store;
23658             this.formatCombo.setValue("");
23659             for (var i =0; i < ans.length;i++) {
23660                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23661                     // select it..
23662                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23663                     break;
23664                 }
23665             }
23666         }
23667         
23668         
23669         
23670         // hides menus... - so this cant be on a menu...
23671         Roo.bootstrap.MenuMgr.hideAll();
23672         */
23673         Roo.bootstrap.MenuMgr.hideAll();
23674         //this.editorsyncValue();
23675     },
23676     onFirstFocus: function() {
23677         this.buttons.each(function(item){
23678            item.enable();
23679         });
23680     },
23681     toggleSourceEdit : function(sourceEditMode){
23682         
23683           
23684         if(sourceEditMode){
23685             Roo.log("disabling buttons");
23686            this.buttons.each( function(item){
23687                 if(item.cmd != 'pencil'){
23688                     item.disable();
23689                 }
23690             });
23691           
23692         }else{
23693             Roo.log("enabling buttons");
23694             if(this.editorcore.initialized){
23695                 this.buttons.each( function(item){
23696                     item.enable();
23697                 });
23698             }
23699             
23700         }
23701         Roo.log("calling toggole on editor");
23702         // tell the editor that it's been pressed..
23703         this.editor.toggleSourceEdit(sourceEditMode);
23704        
23705     }
23706 });
23707
23708
23709
23710
23711
23712 /**
23713  * @class Roo.bootstrap.Table.AbstractSelectionModel
23714  * @extends Roo.util.Observable
23715  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23716  * implemented by descendant classes.  This class should not be directly instantiated.
23717  * @constructor
23718  */
23719 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23720     this.locked = false;
23721     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23722 };
23723
23724
23725 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23726     /** @ignore Called by the grid automatically. Do not call directly. */
23727     init : function(grid){
23728         this.grid = grid;
23729         this.initEvents();
23730     },
23731
23732     /**
23733      * Locks the selections.
23734      */
23735     lock : function(){
23736         this.locked = true;
23737     },
23738
23739     /**
23740      * Unlocks the selections.
23741      */
23742     unlock : function(){
23743         this.locked = false;
23744     },
23745
23746     /**
23747      * Returns true if the selections are locked.
23748      * @return {Boolean}
23749      */
23750     isLocked : function(){
23751         return this.locked;
23752     }
23753 });
23754 /**
23755  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23756  * @class Roo.bootstrap.Table.RowSelectionModel
23757  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23758  * It supports multiple selections and keyboard selection/navigation. 
23759  * @constructor
23760  * @param {Object} config
23761  */
23762
23763 Roo.bootstrap.Table.RowSelectionModel = function(config){
23764     Roo.apply(this, config);
23765     this.selections = new Roo.util.MixedCollection(false, function(o){
23766         return o.id;
23767     });
23768
23769     this.last = false;
23770     this.lastActive = false;
23771
23772     this.addEvents({
23773         /**
23774              * @event selectionchange
23775              * Fires when the selection changes
23776              * @param {SelectionModel} this
23777              */
23778             "selectionchange" : true,
23779         /**
23780              * @event afterselectionchange
23781              * Fires after the selection changes (eg. by key press or clicking)
23782              * @param {SelectionModel} this
23783              */
23784             "afterselectionchange" : true,
23785         /**
23786              * @event beforerowselect
23787              * Fires when a row is selected being selected, return false to cancel.
23788              * @param {SelectionModel} this
23789              * @param {Number} rowIndex The selected index
23790              * @param {Boolean} keepExisting False if other selections will be cleared
23791              */
23792             "beforerowselect" : true,
23793         /**
23794              * @event rowselect
23795              * Fires when a row is selected.
23796              * @param {SelectionModel} this
23797              * @param {Number} rowIndex The selected index
23798              * @param {Roo.data.Record} r The record
23799              */
23800             "rowselect" : true,
23801         /**
23802              * @event rowdeselect
23803              * Fires when a row is deselected.
23804              * @param {SelectionModel} this
23805              * @param {Number} rowIndex The selected index
23806              */
23807         "rowdeselect" : true
23808     });
23809     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23810     this.locked = false;
23811  };
23812
23813 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23814     /**
23815      * @cfg {Boolean} singleSelect
23816      * True to allow selection of only one row at a time (defaults to false)
23817      */
23818     singleSelect : false,
23819
23820     // private
23821     initEvents : function()
23822     {
23823
23824         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23825         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23826         //}else{ // allow click to work like normal
23827          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23828         //}
23829         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23830         this.grid.on("rowclick", this.handleMouseDown, this);
23831         
23832         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23833             "up" : function(e){
23834                 if(!e.shiftKey){
23835                     this.selectPrevious(e.shiftKey);
23836                 }else if(this.last !== false && this.lastActive !== false){
23837                     var last = this.last;
23838                     this.selectRange(this.last,  this.lastActive-1);
23839                     this.grid.getView().focusRow(this.lastActive);
23840                     if(last !== false){
23841                         this.last = last;
23842                     }
23843                 }else{
23844                     this.selectFirstRow();
23845                 }
23846                 this.fireEvent("afterselectionchange", this);
23847             },
23848             "down" : function(e){
23849                 if(!e.shiftKey){
23850                     this.selectNext(e.shiftKey);
23851                 }else if(this.last !== false && this.lastActive !== false){
23852                     var last = this.last;
23853                     this.selectRange(this.last,  this.lastActive+1);
23854                     this.grid.getView().focusRow(this.lastActive);
23855                     if(last !== false){
23856                         this.last = last;
23857                     }
23858                 }else{
23859                     this.selectFirstRow();
23860                 }
23861                 this.fireEvent("afterselectionchange", this);
23862             },
23863             scope: this
23864         });
23865         this.grid.store.on('load', function(){
23866             this.selections.clear();
23867         },this);
23868         /*
23869         var view = this.grid.view;
23870         view.on("refresh", this.onRefresh, this);
23871         view.on("rowupdated", this.onRowUpdated, this);
23872         view.on("rowremoved", this.onRemove, this);
23873         */
23874     },
23875
23876     // private
23877     onRefresh : function()
23878     {
23879         var ds = this.grid.store, i, v = this.grid.view;
23880         var s = this.selections;
23881         s.each(function(r){
23882             if((i = ds.indexOfId(r.id)) != -1){
23883                 v.onRowSelect(i);
23884             }else{
23885                 s.remove(r);
23886             }
23887         });
23888     },
23889
23890     // private
23891     onRemove : function(v, index, r){
23892         this.selections.remove(r);
23893     },
23894
23895     // private
23896     onRowUpdated : function(v, index, r){
23897         if(this.isSelected(r)){
23898             v.onRowSelect(index);
23899         }
23900     },
23901
23902     /**
23903      * Select records.
23904      * @param {Array} records The records to select
23905      * @param {Boolean} keepExisting (optional) True to keep existing selections
23906      */
23907     selectRecords : function(records, keepExisting)
23908     {
23909         if(!keepExisting){
23910             this.clearSelections();
23911         }
23912             var ds = this.grid.store;
23913         for(var i = 0, len = records.length; i < len; i++){
23914             this.selectRow(ds.indexOf(records[i]), true);
23915         }
23916     },
23917
23918     /**
23919      * Gets the number of selected rows.
23920      * @return {Number}
23921      */
23922     getCount : function(){
23923         return this.selections.length;
23924     },
23925
23926     /**
23927      * Selects the first row in the grid.
23928      */
23929     selectFirstRow : function(){
23930         this.selectRow(0);
23931     },
23932
23933     /**
23934      * Select the last row.
23935      * @param {Boolean} keepExisting (optional) True to keep existing selections
23936      */
23937     selectLastRow : function(keepExisting){
23938         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23939         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23940     },
23941
23942     /**
23943      * Selects the row immediately following the last selected row.
23944      * @param {Boolean} keepExisting (optional) True to keep existing selections
23945      */
23946     selectNext : function(keepExisting)
23947     {
23948             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23949             this.selectRow(this.last+1, keepExisting);
23950             this.grid.getView().focusRow(this.last);
23951         }
23952     },
23953
23954     /**
23955      * Selects the row that precedes the last selected row.
23956      * @param {Boolean} keepExisting (optional) True to keep existing selections
23957      */
23958     selectPrevious : function(keepExisting){
23959         if(this.last){
23960             this.selectRow(this.last-1, keepExisting);
23961             this.grid.getView().focusRow(this.last);
23962         }
23963     },
23964
23965     /**
23966      * Returns the selected records
23967      * @return {Array} Array of selected records
23968      */
23969     getSelections : function(){
23970         return [].concat(this.selections.items);
23971     },
23972
23973     /**
23974      * Returns the first selected record.
23975      * @return {Record}
23976      */
23977     getSelected : function(){
23978         return this.selections.itemAt(0);
23979     },
23980
23981
23982     /**
23983      * Clears all selections.
23984      */
23985     clearSelections : function(fast)
23986     {
23987         if(this.locked) {
23988             return;
23989         }
23990         if(fast !== true){
23991                 var ds = this.grid.store;
23992             var s = this.selections;
23993             s.each(function(r){
23994                 this.deselectRow(ds.indexOfId(r.id));
23995             }, this);
23996             s.clear();
23997         }else{
23998             this.selections.clear();
23999         }
24000         this.last = false;
24001     },
24002
24003
24004     /**
24005      * Selects all rows.
24006      */
24007     selectAll : function(){
24008         if(this.locked) {
24009             return;
24010         }
24011         this.selections.clear();
24012         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24013             this.selectRow(i, true);
24014         }
24015     },
24016
24017     /**
24018      * Returns True if there is a selection.
24019      * @return {Boolean}
24020      */
24021     hasSelection : function(){
24022         return this.selections.length > 0;
24023     },
24024
24025     /**
24026      * Returns True if the specified row is selected.
24027      * @param {Number/Record} record The record or index of the record to check
24028      * @return {Boolean}
24029      */
24030     isSelected : function(index){
24031             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24032         return (r && this.selections.key(r.id) ? true : false);
24033     },
24034
24035     /**
24036      * Returns True if the specified record id is selected.
24037      * @param {String} id The id of record to check
24038      * @return {Boolean}
24039      */
24040     isIdSelected : function(id){
24041         return (this.selections.key(id) ? true : false);
24042     },
24043
24044
24045     // private
24046     handleMouseDBClick : function(e, t){
24047         
24048     },
24049     // private
24050     handleMouseDown : function(e, t)
24051     {
24052             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24053         if(this.isLocked() || rowIndex < 0 ){
24054             return;
24055         };
24056         if(e.shiftKey && this.last !== false){
24057             var last = this.last;
24058             this.selectRange(last, rowIndex, e.ctrlKey);
24059             this.last = last; // reset the last
24060             t.focus();
24061     
24062         }else{
24063             var isSelected = this.isSelected(rowIndex);
24064             //Roo.log("select row:" + rowIndex);
24065             if(isSelected){
24066                 this.deselectRow(rowIndex);
24067             } else {
24068                         this.selectRow(rowIndex, true);
24069             }
24070     
24071             /*
24072                 if(e.button !== 0 && isSelected){
24073                 alert('rowIndex 2: ' + rowIndex);
24074                     view.focusRow(rowIndex);
24075                 }else if(e.ctrlKey && isSelected){
24076                     this.deselectRow(rowIndex);
24077                 }else if(!isSelected){
24078                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24079                     view.focusRow(rowIndex);
24080                 }
24081             */
24082         }
24083         this.fireEvent("afterselectionchange", this);
24084     },
24085     // private
24086     handleDragableRowClick :  function(grid, rowIndex, e) 
24087     {
24088         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24089             this.selectRow(rowIndex, false);
24090             grid.view.focusRow(rowIndex);
24091              this.fireEvent("afterselectionchange", this);
24092         }
24093     },
24094     
24095     /**
24096      * Selects multiple rows.
24097      * @param {Array} rows Array of the indexes of the row to select
24098      * @param {Boolean} keepExisting (optional) True to keep existing selections
24099      */
24100     selectRows : function(rows, keepExisting){
24101         if(!keepExisting){
24102             this.clearSelections();
24103         }
24104         for(var i = 0, len = rows.length; i < len; i++){
24105             this.selectRow(rows[i], true);
24106         }
24107     },
24108
24109     /**
24110      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24111      * @param {Number} startRow The index of the first row in the range
24112      * @param {Number} endRow The index of the last row in the range
24113      * @param {Boolean} keepExisting (optional) True to retain existing selections
24114      */
24115     selectRange : function(startRow, endRow, keepExisting){
24116         if(this.locked) {
24117             return;
24118         }
24119         if(!keepExisting){
24120             this.clearSelections();
24121         }
24122         if(startRow <= endRow){
24123             for(var i = startRow; i <= endRow; i++){
24124                 this.selectRow(i, true);
24125             }
24126         }else{
24127             for(var i = startRow; i >= endRow; i--){
24128                 this.selectRow(i, true);
24129             }
24130         }
24131     },
24132
24133     /**
24134      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24135      * @param {Number} startRow The index of the first row in the range
24136      * @param {Number} endRow The index of the last row in the range
24137      */
24138     deselectRange : function(startRow, endRow, preventViewNotify){
24139         if(this.locked) {
24140             return;
24141         }
24142         for(var i = startRow; i <= endRow; i++){
24143             this.deselectRow(i, preventViewNotify);
24144         }
24145     },
24146
24147     /**
24148      * Selects a row.
24149      * @param {Number} row The index of the row to select
24150      * @param {Boolean} keepExisting (optional) True to keep existing selections
24151      */
24152     selectRow : function(index, keepExisting, preventViewNotify)
24153     {
24154             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24155             return;
24156         }
24157         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24158             if(!keepExisting || this.singleSelect){
24159                 this.clearSelections();
24160             }
24161             
24162             var r = this.grid.store.getAt(index);
24163             //console.log('selectRow - record id :' + r.id);
24164             
24165             this.selections.add(r);
24166             this.last = this.lastActive = index;
24167             if(!preventViewNotify){
24168                 var proxy = new Roo.Element(
24169                                 this.grid.getRowDom(index)
24170                 );
24171                 proxy.addClass('bg-info info');
24172             }
24173             this.fireEvent("rowselect", this, index, r);
24174             this.fireEvent("selectionchange", this);
24175         }
24176     },
24177
24178     /**
24179      * Deselects a row.
24180      * @param {Number} row The index of the row to deselect
24181      */
24182     deselectRow : function(index, preventViewNotify)
24183     {
24184         if(this.locked) {
24185             return;
24186         }
24187         if(this.last == index){
24188             this.last = false;
24189         }
24190         if(this.lastActive == index){
24191             this.lastActive = false;
24192         }
24193         
24194         var r = this.grid.store.getAt(index);
24195         if (!r) {
24196             return;
24197         }
24198         
24199         this.selections.remove(r);
24200         //.console.log('deselectRow - record id :' + r.id);
24201         if(!preventViewNotify){
24202         
24203             var proxy = new Roo.Element(
24204                 this.grid.getRowDom(index)
24205             );
24206             proxy.removeClass('bg-info info');
24207         }
24208         this.fireEvent("rowdeselect", this, index);
24209         this.fireEvent("selectionchange", this);
24210     },
24211
24212     // private
24213     restoreLast : function(){
24214         if(this._last){
24215             this.last = this._last;
24216         }
24217     },
24218
24219     // private
24220     acceptsNav : function(row, col, cm){
24221         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24222     },
24223
24224     // private
24225     onEditorKey : function(field, e){
24226         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24227         if(k == e.TAB){
24228             e.stopEvent();
24229             ed.completeEdit();
24230             if(e.shiftKey){
24231                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24232             }else{
24233                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24234             }
24235         }else if(k == e.ENTER && !e.ctrlKey){
24236             e.stopEvent();
24237             ed.completeEdit();
24238             if(e.shiftKey){
24239                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24240             }else{
24241                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24242             }
24243         }else if(k == e.ESC){
24244             ed.cancelEdit();
24245         }
24246         if(newCell){
24247             g.startEditing(newCell[0], newCell[1]);
24248         }
24249     }
24250 });
24251 /*
24252  * Based on:
24253  * Ext JS Library 1.1.1
24254  * Copyright(c) 2006-2007, Ext JS, LLC.
24255  *
24256  * Originally Released Under LGPL - original licence link has changed is not relivant.
24257  *
24258  * Fork - LGPL
24259  * <script type="text/javascript">
24260  */
24261  
24262 /**
24263  * @class Roo.bootstrap.PagingToolbar
24264  * @extends Roo.bootstrap.NavSimplebar
24265  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24266  * @constructor
24267  * Create a new PagingToolbar
24268  * @param {Object} config The config object
24269  * @param {Roo.data.Store} store
24270  */
24271 Roo.bootstrap.PagingToolbar = function(config)
24272 {
24273     // old args format still supported... - xtype is prefered..
24274         // created from xtype...
24275     
24276     this.ds = config.dataSource;
24277     
24278     if (config.store && !this.ds) {
24279         this.store= Roo.factory(config.store, Roo.data);
24280         this.ds = this.store;
24281         this.ds.xmodule = this.xmodule || false;
24282     }
24283     
24284     this.toolbarItems = [];
24285     if (config.items) {
24286         this.toolbarItems = config.items;
24287     }
24288     
24289     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24290     
24291     this.cursor = 0;
24292     
24293     if (this.ds) { 
24294         this.bind(this.ds);
24295     }
24296     
24297     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24298     
24299 };
24300
24301 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24302     /**
24303      * @cfg {Roo.data.Store} dataSource
24304      * The underlying data store providing the paged data
24305      */
24306     /**
24307      * @cfg {String/HTMLElement/Element} container
24308      * container The id or element that will contain the toolbar
24309      */
24310     /**
24311      * @cfg {Boolean} displayInfo
24312      * True to display the displayMsg (defaults to false)
24313      */
24314     /**
24315      * @cfg {Number} pageSize
24316      * The number of records to display per page (defaults to 20)
24317      */
24318     pageSize: 20,
24319     /**
24320      * @cfg {String} displayMsg
24321      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24322      */
24323     displayMsg : 'Displaying {0} - {1} of {2}',
24324     /**
24325      * @cfg {String} emptyMsg
24326      * The message to display when no records are found (defaults to "No data to display")
24327      */
24328     emptyMsg : 'No data to display',
24329     /**
24330      * Customizable piece of the default paging text (defaults to "Page")
24331      * @type String
24332      */
24333     beforePageText : "Page",
24334     /**
24335      * Customizable piece of the default paging text (defaults to "of %0")
24336      * @type String
24337      */
24338     afterPageText : "of {0}",
24339     /**
24340      * Customizable piece of the default paging text (defaults to "First Page")
24341      * @type String
24342      */
24343     firstText : "First Page",
24344     /**
24345      * Customizable piece of the default paging text (defaults to "Previous Page")
24346      * @type String
24347      */
24348     prevText : "Previous Page",
24349     /**
24350      * Customizable piece of the default paging text (defaults to "Next Page")
24351      * @type String
24352      */
24353     nextText : "Next Page",
24354     /**
24355      * Customizable piece of the default paging text (defaults to "Last Page")
24356      * @type String
24357      */
24358     lastText : "Last Page",
24359     /**
24360      * Customizable piece of the default paging text (defaults to "Refresh")
24361      * @type String
24362      */
24363     refreshText : "Refresh",
24364
24365     buttons : false,
24366     // private
24367     onRender : function(ct, position) 
24368     {
24369         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24370         this.navgroup.parentId = this.id;
24371         this.navgroup.onRender(this.el, null);
24372         // add the buttons to the navgroup
24373         
24374         if(this.displayInfo){
24375             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24376             this.displayEl = this.el.select('.x-paging-info', true).first();
24377 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24378 //            this.displayEl = navel.el.select('span',true).first();
24379         }
24380         
24381         var _this = this;
24382         
24383         if(this.buttons){
24384             Roo.each(_this.buttons, function(e){ // this might need to use render????
24385                Roo.factory(e).onRender(_this.el, null);
24386             });
24387         }
24388             
24389         Roo.each(_this.toolbarItems, function(e) {
24390             _this.navgroup.addItem(e);
24391         });
24392         
24393         
24394         this.first = this.navgroup.addItem({
24395             tooltip: this.firstText,
24396             cls: "prev",
24397             icon : 'fa fa-backward',
24398             disabled: true,
24399             preventDefault: true,
24400             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24401         });
24402         
24403         this.prev =  this.navgroup.addItem({
24404             tooltip: this.prevText,
24405             cls: "prev",
24406             icon : 'fa fa-step-backward',
24407             disabled: true,
24408             preventDefault: true,
24409             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24410         });
24411     //this.addSeparator();
24412         
24413         
24414         var field = this.navgroup.addItem( {
24415             tagtype : 'span',
24416             cls : 'x-paging-position',
24417             
24418             html : this.beforePageText  +
24419                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24420                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24421          } ); //?? escaped?
24422         
24423         this.field = field.el.select('input', true).first();
24424         this.field.on("keydown", this.onPagingKeydown, this);
24425         this.field.on("focus", function(){this.dom.select();});
24426     
24427     
24428         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24429         //this.field.setHeight(18);
24430         //this.addSeparator();
24431         this.next = this.navgroup.addItem({
24432             tooltip: this.nextText,
24433             cls: "next",
24434             html : ' <i class="fa fa-step-forward">',
24435             disabled: true,
24436             preventDefault: true,
24437             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24438         });
24439         this.last = this.navgroup.addItem({
24440             tooltip: this.lastText,
24441             icon : 'fa fa-forward',
24442             cls: "next",
24443             disabled: true,
24444             preventDefault: true,
24445             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24446         });
24447     //this.addSeparator();
24448         this.loading = this.navgroup.addItem({
24449             tooltip: this.refreshText,
24450             icon: 'fa fa-refresh',
24451             preventDefault: true,
24452             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24453         });
24454         
24455     },
24456
24457     // private
24458     updateInfo : function(){
24459         if(this.displayEl){
24460             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24461             var msg = count == 0 ?
24462                 this.emptyMsg :
24463                 String.format(
24464                     this.displayMsg,
24465                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24466                 );
24467             this.displayEl.update(msg);
24468         }
24469     },
24470
24471     // private
24472     onLoad : function(ds, r, o)
24473     {
24474         this.cursor = o.params.start ? o.params.start : 0;
24475         
24476         var d = this.getPageData(),
24477             ap = d.activePage,
24478             ps = d.pages;
24479         
24480         
24481         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24482         this.field.dom.value = ap;
24483         this.first.setDisabled(ap == 1);
24484         this.prev.setDisabled(ap == 1);
24485         this.next.setDisabled(ap == ps);
24486         this.last.setDisabled(ap == ps);
24487         this.loading.enable();
24488         this.updateInfo();
24489     },
24490
24491     // private
24492     getPageData : function(){
24493         var total = this.ds.getTotalCount();
24494         return {
24495             total : total,
24496             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24497             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24498         };
24499     },
24500
24501     // private
24502     onLoadError : function(){
24503         this.loading.enable();
24504     },
24505
24506     // private
24507     onPagingKeydown : function(e){
24508         var k = e.getKey();
24509         var d = this.getPageData();
24510         if(k == e.RETURN){
24511             var v = this.field.dom.value, pageNum;
24512             if(!v || isNaN(pageNum = parseInt(v, 10))){
24513                 this.field.dom.value = d.activePage;
24514                 return;
24515             }
24516             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24517             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24518             e.stopEvent();
24519         }
24520         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))
24521         {
24522           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24523           this.field.dom.value = pageNum;
24524           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24525           e.stopEvent();
24526         }
24527         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24528         {
24529           var v = this.field.dom.value, pageNum; 
24530           var increment = (e.shiftKey) ? 10 : 1;
24531           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24532                 increment *= -1;
24533           }
24534           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24535             this.field.dom.value = d.activePage;
24536             return;
24537           }
24538           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24539           {
24540             this.field.dom.value = parseInt(v, 10) + increment;
24541             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24542             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24543           }
24544           e.stopEvent();
24545         }
24546     },
24547
24548     // private
24549     beforeLoad : function(){
24550         if(this.loading){
24551             this.loading.disable();
24552         }
24553     },
24554
24555     // private
24556     onClick : function(which){
24557         
24558         var ds = this.ds;
24559         if (!ds) {
24560             return;
24561         }
24562         
24563         switch(which){
24564             case "first":
24565                 ds.load({params:{start: 0, limit: this.pageSize}});
24566             break;
24567             case "prev":
24568                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24569             break;
24570             case "next":
24571                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24572             break;
24573             case "last":
24574                 var total = ds.getTotalCount();
24575                 var extra = total % this.pageSize;
24576                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24577                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24578             break;
24579             case "refresh":
24580                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24581             break;
24582         }
24583     },
24584
24585     /**
24586      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24587      * @param {Roo.data.Store} store The data store to unbind
24588      */
24589     unbind : function(ds){
24590         ds.un("beforeload", this.beforeLoad, this);
24591         ds.un("load", this.onLoad, this);
24592         ds.un("loadexception", this.onLoadError, this);
24593         ds.un("remove", this.updateInfo, this);
24594         ds.un("add", this.updateInfo, this);
24595         this.ds = undefined;
24596     },
24597
24598     /**
24599      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24600      * @param {Roo.data.Store} store The data store to bind
24601      */
24602     bind : function(ds){
24603         ds.on("beforeload", this.beforeLoad, this);
24604         ds.on("load", this.onLoad, this);
24605         ds.on("loadexception", this.onLoadError, this);
24606         ds.on("remove", this.updateInfo, this);
24607         ds.on("add", this.updateInfo, this);
24608         this.ds = ds;
24609     }
24610 });/*
24611  * - LGPL
24612  *
24613  * element
24614  * 
24615  */
24616
24617 /**
24618  * @class Roo.bootstrap.MessageBar
24619  * @extends Roo.bootstrap.Component
24620  * Bootstrap MessageBar class
24621  * @cfg {String} html contents of the MessageBar
24622  * @cfg {String} weight (info | success | warning | danger) default info
24623  * @cfg {String} beforeClass insert the bar before the given class
24624  * @cfg {Boolean} closable (true | false) default false
24625  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24626  * 
24627  * @constructor
24628  * Create a new Element
24629  * @param {Object} config The config object
24630  */
24631
24632 Roo.bootstrap.MessageBar = function(config){
24633     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24634 };
24635
24636 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24637     
24638     html: '',
24639     weight: 'info',
24640     closable: false,
24641     fixed: false,
24642     beforeClass: 'bootstrap-sticky-wrap',
24643     
24644     getAutoCreate : function(){
24645         
24646         var cfg = {
24647             tag: 'div',
24648             cls: 'alert alert-dismissable alert-' + this.weight,
24649             cn: [
24650                 {
24651                     tag: 'span',
24652                     cls: 'message',
24653                     html: this.html || ''
24654                 }
24655             ]
24656         };
24657         
24658         if(this.fixed){
24659             cfg.cls += ' alert-messages-fixed';
24660         }
24661         
24662         if(this.closable){
24663             cfg.cn.push({
24664                 tag: 'button',
24665                 cls: 'close',
24666                 html: 'x'
24667             });
24668         }
24669         
24670         return cfg;
24671     },
24672     
24673     onRender : function(ct, position)
24674     {
24675         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24676         
24677         if(!this.el){
24678             var cfg = Roo.apply({},  this.getAutoCreate());
24679             cfg.id = Roo.id();
24680             
24681             if (this.cls) {
24682                 cfg.cls += ' ' + this.cls;
24683             }
24684             if (this.style) {
24685                 cfg.style = this.style;
24686             }
24687             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24688             
24689             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24690         }
24691         
24692         this.el.select('>button.close').on('click', this.hide, this);
24693         
24694     },
24695     
24696     show : function()
24697     {
24698         if (!this.rendered) {
24699             this.render();
24700         }
24701         
24702         this.el.show();
24703         
24704         this.fireEvent('show', this);
24705         
24706     },
24707     
24708     hide : function()
24709     {
24710         if (!this.rendered) {
24711             this.render();
24712         }
24713         
24714         this.el.hide();
24715         
24716         this.fireEvent('hide', this);
24717     },
24718     
24719     update : function()
24720     {
24721 //        var e = this.el.dom.firstChild;
24722 //        
24723 //        if(this.closable){
24724 //            e = e.nextSibling;
24725 //        }
24726 //        
24727 //        e.data = this.html || '';
24728
24729         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24730     }
24731    
24732 });
24733
24734  
24735
24736      /*
24737  * - LGPL
24738  *
24739  * Graph
24740  * 
24741  */
24742
24743
24744 /**
24745  * @class Roo.bootstrap.Graph
24746  * @extends Roo.bootstrap.Component
24747  * Bootstrap Graph class
24748 > Prameters
24749  -sm {number} sm 4
24750  -md {number} md 5
24751  @cfg {String} graphtype  bar | vbar | pie
24752  @cfg {number} g_x coodinator | centre x (pie)
24753  @cfg {number} g_y coodinator | centre y (pie)
24754  @cfg {number} g_r radius (pie)
24755  @cfg {number} g_height height of the chart (respected by all elements in the set)
24756  @cfg {number} g_width width of the chart (respected by all elements in the set)
24757  @cfg {Object} title The title of the chart
24758     
24759  -{Array}  values
24760  -opts (object) options for the chart 
24761      o {
24762      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24763      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24764      o vgutter (number)
24765      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.
24766      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24767      o to
24768      o stretch (boolean)
24769      o }
24770  -opts (object) options for the pie
24771      o{
24772      o cut
24773      o startAngle (number)
24774      o endAngle (number)
24775      } 
24776  *
24777  * @constructor
24778  * Create a new Input
24779  * @param {Object} config The config object
24780  */
24781
24782 Roo.bootstrap.Graph = function(config){
24783     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24784     
24785     this.addEvents({
24786         // img events
24787         /**
24788          * @event click
24789          * The img click event for the img.
24790          * @param {Roo.EventObject} e
24791          */
24792         "click" : true
24793     });
24794 };
24795
24796 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24797     
24798     sm: 4,
24799     md: 5,
24800     graphtype: 'bar',
24801     g_height: 250,
24802     g_width: 400,
24803     g_x: 50,
24804     g_y: 50,
24805     g_r: 30,
24806     opts:{
24807         //g_colors: this.colors,
24808         g_type: 'soft',
24809         g_gutter: '20%'
24810
24811     },
24812     title : false,
24813
24814     getAutoCreate : function(){
24815         
24816         var cfg = {
24817             tag: 'div',
24818             html : null
24819         };
24820         
24821         
24822         return  cfg;
24823     },
24824
24825     onRender : function(ct,position){
24826         
24827         
24828         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24829         
24830         if (typeof(Raphael) == 'undefined') {
24831             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24832             return;
24833         }
24834         
24835         this.raphael = Raphael(this.el.dom);
24836         
24837                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24838                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24839                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24840                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24841                 /*
24842                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24843                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24844                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24845                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24846                 
24847                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24848                 r.barchart(330, 10, 300, 220, data1);
24849                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24850                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24851                 */
24852                 
24853                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24854                 // r.barchart(30, 30, 560, 250,  xdata, {
24855                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24856                 //     axis : "0 0 1 1",
24857                 //     axisxlabels :  xdata
24858                 //     //yvalues : cols,
24859                    
24860                 // });
24861 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24862 //        
24863 //        this.load(null,xdata,{
24864 //                axis : "0 0 1 1",
24865 //                axisxlabels :  xdata
24866 //                });
24867
24868     },
24869
24870     load : function(graphtype,xdata,opts)
24871     {
24872         this.raphael.clear();
24873         if(!graphtype) {
24874             graphtype = this.graphtype;
24875         }
24876         if(!opts){
24877             opts = this.opts;
24878         }
24879         var r = this.raphael,
24880             fin = function () {
24881                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24882             },
24883             fout = function () {
24884                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24885             },
24886             pfin = function() {
24887                 this.sector.stop();
24888                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24889
24890                 if (this.label) {
24891                     this.label[0].stop();
24892                     this.label[0].attr({ r: 7.5 });
24893                     this.label[1].attr({ "font-weight": 800 });
24894                 }
24895             },
24896             pfout = function() {
24897                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24898
24899                 if (this.label) {
24900                     this.label[0].animate({ r: 5 }, 500, "bounce");
24901                     this.label[1].attr({ "font-weight": 400 });
24902                 }
24903             };
24904
24905         switch(graphtype){
24906             case 'bar':
24907                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24908                 break;
24909             case 'hbar':
24910                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24911                 break;
24912             case 'pie':
24913 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24914 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24915 //            
24916                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24917                 
24918                 break;
24919
24920         }
24921         
24922         if(this.title){
24923             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24924         }
24925         
24926     },
24927     
24928     setTitle: function(o)
24929     {
24930         this.title = o;
24931     },
24932     
24933     initEvents: function() {
24934         
24935         if(!this.href){
24936             this.el.on('click', this.onClick, this);
24937         }
24938     },
24939     
24940     onClick : function(e)
24941     {
24942         Roo.log('img onclick');
24943         this.fireEvent('click', this, e);
24944     }
24945    
24946 });
24947
24948  
24949 /*
24950  * - LGPL
24951  *
24952  * numberBox
24953  * 
24954  */
24955 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24956
24957 /**
24958  * @class Roo.bootstrap.dash.NumberBox
24959  * @extends Roo.bootstrap.Component
24960  * Bootstrap NumberBox class
24961  * @cfg {String} headline Box headline
24962  * @cfg {String} content Box content
24963  * @cfg {String} icon Box icon
24964  * @cfg {String} footer Footer text
24965  * @cfg {String} fhref Footer href
24966  * 
24967  * @constructor
24968  * Create a new NumberBox
24969  * @param {Object} config The config object
24970  */
24971
24972
24973 Roo.bootstrap.dash.NumberBox = function(config){
24974     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24975     
24976 };
24977
24978 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24979     
24980     headline : '',
24981     content : '',
24982     icon : '',
24983     footer : '',
24984     fhref : '',
24985     ficon : '',
24986     
24987     getAutoCreate : function(){
24988         
24989         var cfg = {
24990             tag : 'div',
24991             cls : 'small-box ',
24992             cn : [
24993                 {
24994                     tag : 'div',
24995                     cls : 'inner',
24996                     cn :[
24997                         {
24998                             tag : 'h3',
24999                             cls : 'roo-headline',
25000                             html : this.headline
25001                         },
25002                         {
25003                             tag : 'p',
25004                             cls : 'roo-content',
25005                             html : this.content
25006                         }
25007                     ]
25008                 }
25009             ]
25010         };
25011         
25012         if(this.icon){
25013             cfg.cn.push({
25014                 tag : 'div',
25015                 cls : 'icon',
25016                 cn :[
25017                     {
25018                         tag : 'i',
25019                         cls : 'ion ' + this.icon
25020                     }
25021                 ]
25022             });
25023         }
25024         
25025         if(this.footer){
25026             var footer = {
25027                 tag : 'a',
25028                 cls : 'small-box-footer',
25029                 href : this.fhref || '#',
25030                 html : this.footer
25031             };
25032             
25033             cfg.cn.push(footer);
25034             
25035         }
25036         
25037         return  cfg;
25038     },
25039
25040     onRender : function(ct,position){
25041         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25042
25043
25044        
25045                 
25046     },
25047
25048     setHeadline: function (value)
25049     {
25050         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25051     },
25052     
25053     setFooter: function (value, href)
25054     {
25055         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25056         
25057         if(href){
25058             this.el.select('a.small-box-footer',true).first().attr('href', href);
25059         }
25060         
25061     },
25062
25063     setContent: function (value)
25064     {
25065         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25066     },
25067
25068     initEvents: function() 
25069     {   
25070         
25071     }
25072     
25073 });
25074
25075  
25076 /*
25077  * - LGPL
25078  *
25079  * TabBox
25080  * 
25081  */
25082 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25083
25084 /**
25085  * @class Roo.bootstrap.dash.TabBox
25086  * @extends Roo.bootstrap.Component
25087  * Bootstrap TabBox class
25088  * @cfg {String} title Title of the TabBox
25089  * @cfg {String} icon Icon of the TabBox
25090  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25091  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25092  * 
25093  * @constructor
25094  * Create a new TabBox
25095  * @param {Object} config The config object
25096  */
25097
25098
25099 Roo.bootstrap.dash.TabBox = function(config){
25100     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25101     this.addEvents({
25102         // raw events
25103         /**
25104          * @event addpane
25105          * When a pane is added
25106          * @param {Roo.bootstrap.dash.TabPane} pane
25107          */
25108         "addpane" : true,
25109         /**
25110          * @event activatepane
25111          * When a pane is activated
25112          * @param {Roo.bootstrap.dash.TabPane} pane
25113          */
25114         "activatepane" : true
25115         
25116          
25117     });
25118     
25119     this.panes = [];
25120 };
25121
25122 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25123
25124     title : '',
25125     icon : false,
25126     showtabs : true,
25127     tabScrollable : false,
25128     
25129     getChildContainer : function()
25130     {
25131         return this.el.select('.tab-content', true).first();
25132     },
25133     
25134     getAutoCreate : function(){
25135         
25136         var header = {
25137             tag: 'li',
25138             cls: 'pull-left header',
25139             html: this.title,
25140             cn : []
25141         };
25142         
25143         if(this.icon){
25144             header.cn.push({
25145                 tag: 'i',
25146                 cls: 'fa ' + this.icon
25147             });
25148         }
25149         
25150         var h = {
25151             tag: 'ul',
25152             cls: 'nav nav-tabs pull-right',
25153             cn: [
25154                 header
25155             ]
25156         };
25157         
25158         if(this.tabScrollable){
25159             h = {
25160                 tag: 'div',
25161                 cls: 'tab-header',
25162                 cn: [
25163                     {
25164                         tag: 'ul',
25165                         cls: 'nav nav-tabs pull-right',
25166                         cn: [
25167                             header
25168                         ]
25169                     }
25170                 ]
25171             };
25172         }
25173         
25174         var cfg = {
25175             tag: 'div',
25176             cls: 'nav-tabs-custom',
25177             cn: [
25178                 h,
25179                 {
25180                     tag: 'div',
25181                     cls: 'tab-content no-padding',
25182                     cn: []
25183                 }
25184             ]
25185         };
25186
25187         return  cfg;
25188     },
25189     initEvents : function()
25190     {
25191         //Roo.log('add add pane handler');
25192         this.on('addpane', this.onAddPane, this);
25193     },
25194      /**
25195      * Updates the box title
25196      * @param {String} html to set the title to.
25197      */
25198     setTitle : function(value)
25199     {
25200         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25201     },
25202     onAddPane : function(pane)
25203     {
25204         this.panes.push(pane);
25205         //Roo.log('addpane');
25206         //Roo.log(pane);
25207         // tabs are rendere left to right..
25208         if(!this.showtabs){
25209             return;
25210         }
25211         
25212         var ctr = this.el.select('.nav-tabs', true).first();
25213          
25214          
25215         var existing = ctr.select('.nav-tab',true);
25216         var qty = existing.getCount();;
25217         
25218         
25219         var tab = ctr.createChild({
25220             tag : 'li',
25221             cls : 'nav-tab' + (qty ? '' : ' active'),
25222             cn : [
25223                 {
25224                     tag : 'a',
25225                     href:'#',
25226                     html : pane.title
25227                 }
25228             ]
25229         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25230         pane.tab = tab;
25231         
25232         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25233         if (!qty) {
25234             pane.el.addClass('active');
25235         }
25236         
25237                 
25238     },
25239     onTabClick : function(ev,un,ob,pane)
25240     {
25241         //Roo.log('tab - prev default');
25242         ev.preventDefault();
25243         
25244         
25245         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25246         pane.tab.addClass('active');
25247         //Roo.log(pane.title);
25248         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25249         // technically we should have a deactivate event.. but maybe add later.
25250         // and it should not de-activate the selected tab...
25251         this.fireEvent('activatepane', pane);
25252         pane.el.addClass('active');
25253         pane.fireEvent('activate');
25254         
25255         
25256     },
25257     
25258     getActivePane : function()
25259     {
25260         var r = false;
25261         Roo.each(this.panes, function(p) {
25262             if(p.el.hasClass('active')){
25263                 r = p;
25264                 return false;
25265             }
25266             
25267             return;
25268         });
25269         
25270         return r;
25271     }
25272     
25273     
25274 });
25275
25276  
25277 /*
25278  * - LGPL
25279  *
25280  * Tab pane
25281  * 
25282  */
25283 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25284 /**
25285  * @class Roo.bootstrap.TabPane
25286  * @extends Roo.bootstrap.Component
25287  * Bootstrap TabPane class
25288  * @cfg {Boolean} active (false | true) Default false
25289  * @cfg {String} title title of panel
25290
25291  * 
25292  * @constructor
25293  * Create a new TabPane
25294  * @param {Object} config The config object
25295  */
25296
25297 Roo.bootstrap.dash.TabPane = function(config){
25298     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25299     
25300     this.addEvents({
25301         // raw events
25302         /**
25303          * @event activate
25304          * When a pane is activated
25305          * @param {Roo.bootstrap.dash.TabPane} pane
25306          */
25307         "activate" : true
25308          
25309     });
25310 };
25311
25312 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25313     
25314     active : false,
25315     title : '',
25316     
25317     // the tabBox that this is attached to.
25318     tab : false,
25319      
25320     getAutoCreate : function() 
25321     {
25322         var cfg = {
25323             tag: 'div',
25324             cls: 'tab-pane'
25325         };
25326         
25327         if(this.active){
25328             cfg.cls += ' active';
25329         }
25330         
25331         return cfg;
25332     },
25333     initEvents  : function()
25334     {
25335         //Roo.log('trigger add pane handler');
25336         this.parent().fireEvent('addpane', this)
25337     },
25338     
25339      /**
25340      * Updates the tab title 
25341      * @param {String} html to set the title to.
25342      */
25343     setTitle: function(str)
25344     {
25345         if (!this.tab) {
25346             return;
25347         }
25348         this.title = str;
25349         this.tab.select('a', true).first().dom.innerHTML = str;
25350         
25351     }
25352     
25353     
25354     
25355 });
25356
25357  
25358
25359
25360  /*
25361  * - LGPL
25362  *
25363  * menu
25364  * 
25365  */
25366 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25367
25368 /**
25369  * @class Roo.bootstrap.menu.Menu
25370  * @extends Roo.bootstrap.Component
25371  * Bootstrap Menu class - container for Menu
25372  * @cfg {String} html Text of the menu
25373  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25374  * @cfg {String} icon Font awesome icon
25375  * @cfg {String} pos Menu align to (top | bottom) default bottom
25376  * 
25377  * 
25378  * @constructor
25379  * Create a new Menu
25380  * @param {Object} config The config object
25381  */
25382
25383
25384 Roo.bootstrap.menu.Menu = function(config){
25385     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25386     
25387     this.addEvents({
25388         /**
25389          * @event beforeshow
25390          * Fires before this menu is displayed
25391          * @param {Roo.bootstrap.menu.Menu} this
25392          */
25393         beforeshow : true,
25394         /**
25395          * @event beforehide
25396          * Fires before this menu is hidden
25397          * @param {Roo.bootstrap.menu.Menu} this
25398          */
25399         beforehide : true,
25400         /**
25401          * @event show
25402          * Fires after this menu is displayed
25403          * @param {Roo.bootstrap.menu.Menu} this
25404          */
25405         show : true,
25406         /**
25407          * @event hide
25408          * Fires after this menu is hidden
25409          * @param {Roo.bootstrap.menu.Menu} this
25410          */
25411         hide : true,
25412         /**
25413          * @event click
25414          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25415          * @param {Roo.bootstrap.menu.Menu} this
25416          * @param {Roo.EventObject} e
25417          */
25418         click : true
25419     });
25420     
25421 };
25422
25423 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25424     
25425     submenu : false,
25426     html : '',
25427     weight : 'default',
25428     icon : false,
25429     pos : 'bottom',
25430     
25431     
25432     getChildContainer : function() {
25433         if(this.isSubMenu){
25434             return this.el;
25435         }
25436         
25437         return this.el.select('ul.dropdown-menu', true).first();  
25438     },
25439     
25440     getAutoCreate : function()
25441     {
25442         var text = [
25443             {
25444                 tag : 'span',
25445                 cls : 'roo-menu-text',
25446                 html : this.html
25447             }
25448         ];
25449         
25450         if(this.icon){
25451             text.unshift({
25452                 tag : 'i',
25453                 cls : 'fa ' + this.icon
25454             })
25455         }
25456         
25457         
25458         var cfg = {
25459             tag : 'div',
25460             cls : 'btn-group',
25461             cn : [
25462                 {
25463                     tag : 'button',
25464                     cls : 'dropdown-button btn btn-' + this.weight,
25465                     cn : text
25466                 },
25467                 {
25468                     tag : 'button',
25469                     cls : 'dropdown-toggle btn btn-' + this.weight,
25470                     cn : [
25471                         {
25472                             tag : 'span',
25473                             cls : 'caret'
25474                         }
25475                     ]
25476                 },
25477                 {
25478                     tag : 'ul',
25479                     cls : 'dropdown-menu'
25480                 }
25481             ]
25482             
25483         };
25484         
25485         if(this.pos == 'top'){
25486             cfg.cls += ' dropup';
25487         }
25488         
25489         if(this.isSubMenu){
25490             cfg = {
25491                 tag : 'ul',
25492                 cls : 'dropdown-menu'
25493             }
25494         }
25495         
25496         return cfg;
25497     },
25498     
25499     onRender : function(ct, position)
25500     {
25501         this.isSubMenu = ct.hasClass('dropdown-submenu');
25502         
25503         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25504     },
25505     
25506     initEvents : function() 
25507     {
25508         if(this.isSubMenu){
25509             return;
25510         }
25511         
25512         this.hidden = true;
25513         
25514         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25515         this.triggerEl.on('click', this.onTriggerPress, this);
25516         
25517         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25518         this.buttonEl.on('click', this.onClick, this);
25519         
25520     },
25521     
25522     list : function()
25523     {
25524         if(this.isSubMenu){
25525             return this.el;
25526         }
25527         
25528         return this.el.select('ul.dropdown-menu', true).first();
25529     },
25530     
25531     onClick : function(e)
25532     {
25533         this.fireEvent("click", this, e);
25534     },
25535     
25536     onTriggerPress  : function(e)
25537     {   
25538         if (this.isVisible()) {
25539             this.hide();
25540         } else {
25541             this.show();
25542         }
25543     },
25544     
25545     isVisible : function(){
25546         return !this.hidden;
25547     },
25548     
25549     show : function()
25550     {
25551         this.fireEvent("beforeshow", this);
25552         
25553         this.hidden = false;
25554         this.el.addClass('open');
25555         
25556         Roo.get(document).on("mouseup", this.onMouseUp, this);
25557         
25558         this.fireEvent("show", this);
25559         
25560         
25561     },
25562     
25563     hide : function()
25564     {
25565         this.fireEvent("beforehide", this);
25566         
25567         this.hidden = true;
25568         this.el.removeClass('open');
25569         
25570         Roo.get(document).un("mouseup", this.onMouseUp);
25571         
25572         this.fireEvent("hide", this);
25573     },
25574     
25575     onMouseUp : function()
25576     {
25577         this.hide();
25578     }
25579     
25580 });
25581
25582  
25583  /*
25584  * - LGPL
25585  *
25586  * menu item
25587  * 
25588  */
25589 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25590
25591 /**
25592  * @class Roo.bootstrap.menu.Item
25593  * @extends Roo.bootstrap.Component
25594  * Bootstrap MenuItem class
25595  * @cfg {Boolean} submenu (true | false) default false
25596  * @cfg {String} html text of the item
25597  * @cfg {String} href the link
25598  * @cfg {Boolean} disable (true | false) default false
25599  * @cfg {Boolean} preventDefault (true | false) default true
25600  * @cfg {String} icon Font awesome icon
25601  * @cfg {String} pos Submenu align to (left | right) default right 
25602  * 
25603  * 
25604  * @constructor
25605  * Create a new Item
25606  * @param {Object} config The config object
25607  */
25608
25609
25610 Roo.bootstrap.menu.Item = function(config){
25611     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25612     this.addEvents({
25613         /**
25614          * @event mouseover
25615          * Fires when the mouse is hovering over this menu
25616          * @param {Roo.bootstrap.menu.Item} this
25617          * @param {Roo.EventObject} e
25618          */
25619         mouseover : true,
25620         /**
25621          * @event mouseout
25622          * Fires when the mouse exits this menu
25623          * @param {Roo.bootstrap.menu.Item} this
25624          * @param {Roo.EventObject} e
25625          */
25626         mouseout : true,
25627         // raw events
25628         /**
25629          * @event click
25630          * The raw click event for the entire grid.
25631          * @param {Roo.EventObject} e
25632          */
25633         click : true
25634     });
25635 };
25636
25637 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25638     
25639     submenu : false,
25640     href : '',
25641     html : '',
25642     preventDefault: true,
25643     disable : false,
25644     icon : false,
25645     pos : 'right',
25646     
25647     getAutoCreate : function()
25648     {
25649         var text = [
25650             {
25651                 tag : 'span',
25652                 cls : 'roo-menu-item-text',
25653                 html : this.html
25654             }
25655         ];
25656         
25657         if(this.icon){
25658             text.unshift({
25659                 tag : 'i',
25660                 cls : 'fa ' + this.icon
25661             })
25662         }
25663         
25664         var cfg = {
25665             tag : 'li',
25666             cn : [
25667                 {
25668                     tag : 'a',
25669                     href : this.href || '#',
25670                     cn : text
25671                 }
25672             ]
25673         };
25674         
25675         if(this.disable){
25676             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25677         }
25678         
25679         if(this.submenu){
25680             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25681             
25682             if(this.pos == 'left'){
25683                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25684             }
25685         }
25686         
25687         return cfg;
25688     },
25689     
25690     initEvents : function() 
25691     {
25692         this.el.on('mouseover', this.onMouseOver, this);
25693         this.el.on('mouseout', this.onMouseOut, this);
25694         
25695         this.el.select('a', true).first().on('click', this.onClick, this);
25696         
25697     },
25698     
25699     onClick : function(e)
25700     {
25701         if(this.preventDefault){
25702             e.preventDefault();
25703         }
25704         
25705         this.fireEvent("click", this, e);
25706     },
25707     
25708     onMouseOver : function(e)
25709     {
25710         if(this.submenu && this.pos == 'left'){
25711             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25712         }
25713         
25714         this.fireEvent("mouseover", this, e);
25715     },
25716     
25717     onMouseOut : function(e)
25718     {
25719         this.fireEvent("mouseout", this, e);
25720     }
25721 });
25722
25723  
25724
25725  /*
25726  * - LGPL
25727  *
25728  * menu separator
25729  * 
25730  */
25731 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25732
25733 /**
25734  * @class Roo.bootstrap.menu.Separator
25735  * @extends Roo.bootstrap.Component
25736  * Bootstrap Separator class
25737  * 
25738  * @constructor
25739  * Create a new Separator
25740  * @param {Object} config The config object
25741  */
25742
25743
25744 Roo.bootstrap.menu.Separator = function(config){
25745     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25746 };
25747
25748 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25749     
25750     getAutoCreate : function(){
25751         var cfg = {
25752             tag : 'li',
25753             cls: 'divider'
25754         };
25755         
25756         return cfg;
25757     }
25758    
25759 });
25760
25761  
25762
25763  /*
25764  * - LGPL
25765  *
25766  * Tooltip
25767  * 
25768  */
25769
25770 /**
25771  * @class Roo.bootstrap.Tooltip
25772  * Bootstrap Tooltip class
25773  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25774  * to determine which dom element triggers the tooltip.
25775  * 
25776  * It needs to add support for additional attributes like tooltip-position
25777  * 
25778  * @constructor
25779  * Create a new Toolti
25780  * @param {Object} config The config object
25781  */
25782
25783 Roo.bootstrap.Tooltip = function(config){
25784     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25785     
25786     this.alignment = Roo.bootstrap.Tooltip.alignment;
25787     
25788     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25789         this.alignment = config.alignment;
25790     }
25791     
25792 };
25793
25794 Roo.apply(Roo.bootstrap.Tooltip, {
25795     /**
25796      * @function init initialize tooltip monitoring.
25797      * @static
25798      */
25799     currentEl : false,
25800     currentTip : false,
25801     currentRegion : false,
25802     
25803     //  init : delay?
25804     
25805     init : function()
25806     {
25807         Roo.get(document).on('mouseover', this.enter ,this);
25808         Roo.get(document).on('mouseout', this.leave, this);
25809          
25810         
25811         this.currentTip = new Roo.bootstrap.Tooltip();
25812     },
25813     
25814     enter : function(ev)
25815     {
25816         var dom = ev.getTarget();
25817         
25818         //Roo.log(['enter',dom]);
25819         var el = Roo.fly(dom);
25820         if (this.currentEl) {
25821             //Roo.log(dom);
25822             //Roo.log(this.currentEl);
25823             //Roo.log(this.currentEl.contains(dom));
25824             if (this.currentEl == el) {
25825                 return;
25826             }
25827             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25828                 return;
25829             }
25830
25831         }
25832         
25833         if (this.currentTip.el) {
25834             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25835         }    
25836         //Roo.log(ev);
25837         
25838         if(!el || el.dom == document){
25839             return;
25840         }
25841         
25842         var bindEl = el;
25843         
25844         // you can not look for children, as if el is the body.. then everythign is the child..
25845         if (!el.attr('tooltip')) { //
25846             if (!el.select("[tooltip]").elements.length) {
25847                 return;
25848             }
25849             // is the mouse over this child...?
25850             bindEl = el.select("[tooltip]").first();
25851             var xy = ev.getXY();
25852             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25853                 //Roo.log("not in region.");
25854                 return;
25855             }
25856             //Roo.log("child element over..");
25857             
25858         }
25859         this.currentEl = bindEl;
25860         this.currentTip.bind(bindEl);
25861         this.currentRegion = Roo.lib.Region.getRegion(dom);
25862         this.currentTip.enter();
25863         
25864     },
25865     leave : function(ev)
25866     {
25867         var dom = ev.getTarget();
25868         //Roo.log(['leave',dom]);
25869         if (!this.currentEl) {
25870             return;
25871         }
25872         
25873         
25874         if (dom != this.currentEl.dom) {
25875             return;
25876         }
25877         var xy = ev.getXY();
25878         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25879             return;
25880         }
25881         // only activate leave if mouse cursor is outside... bounding box..
25882         
25883         
25884         
25885         
25886         if (this.currentTip) {
25887             this.currentTip.leave();
25888         }
25889         //Roo.log('clear currentEl');
25890         this.currentEl = false;
25891         
25892         
25893     },
25894     alignment : {
25895         'left' : ['r-l', [-2,0], 'right'],
25896         'right' : ['l-r', [2,0], 'left'],
25897         'bottom' : ['t-b', [0,2], 'top'],
25898         'top' : [ 'b-t', [0,-2], 'bottom']
25899     }
25900     
25901 });
25902
25903
25904 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25905     
25906     
25907     bindEl : false,
25908     
25909     delay : null, // can be { show : 300 , hide: 500}
25910     
25911     timeout : null,
25912     
25913     hoverState : null, //???
25914     
25915     placement : 'bottom', 
25916     
25917     alignment : false,
25918     
25919     getAutoCreate : function(){
25920     
25921         var cfg = {
25922            cls : 'tooltip',
25923            role : 'tooltip',
25924            cn : [
25925                 {
25926                     cls : 'tooltip-arrow'
25927                 },
25928                 {
25929                     cls : 'tooltip-inner'
25930                 }
25931            ]
25932         };
25933         
25934         return cfg;
25935     },
25936     bind : function(el)
25937     {
25938         this.bindEl = el;
25939     },
25940       
25941     
25942     enter : function () {
25943        
25944         if (this.timeout != null) {
25945             clearTimeout(this.timeout);
25946         }
25947         
25948         this.hoverState = 'in';
25949          //Roo.log("enter - show");
25950         if (!this.delay || !this.delay.show) {
25951             this.show();
25952             return;
25953         }
25954         var _t = this;
25955         this.timeout = setTimeout(function () {
25956             if (_t.hoverState == 'in') {
25957                 _t.show();
25958             }
25959         }, this.delay.show);
25960     },
25961     leave : function()
25962     {
25963         clearTimeout(this.timeout);
25964     
25965         this.hoverState = 'out';
25966          if (!this.delay || !this.delay.hide) {
25967             this.hide();
25968             return;
25969         }
25970        
25971         var _t = this;
25972         this.timeout = setTimeout(function () {
25973             //Roo.log("leave - timeout");
25974             
25975             if (_t.hoverState == 'out') {
25976                 _t.hide();
25977                 Roo.bootstrap.Tooltip.currentEl = false;
25978             }
25979         }, delay);
25980     },
25981     
25982     show : function (msg)
25983     {
25984         if (!this.el) {
25985             this.render(document.body);
25986         }
25987         // set content.
25988         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25989         
25990         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25991         
25992         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25993         
25994         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25995         
25996         var placement = typeof this.placement == 'function' ?
25997             this.placement.call(this, this.el, on_el) :
25998             this.placement;
25999             
26000         var autoToken = /\s?auto?\s?/i;
26001         var autoPlace = autoToken.test(placement);
26002         if (autoPlace) {
26003             placement = placement.replace(autoToken, '') || 'top';
26004         }
26005         
26006         //this.el.detach()
26007         //this.el.setXY([0,0]);
26008         this.el.show();
26009         //this.el.dom.style.display='block';
26010         
26011         //this.el.appendTo(on_el);
26012         
26013         var p = this.getPosition();
26014         var box = this.el.getBox();
26015         
26016         if (autoPlace) {
26017             // fixme..
26018         }
26019         
26020         var align = this.alignment[placement];
26021         
26022         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26023         
26024         if(placement == 'top' || placement == 'bottom'){
26025             if(xy[0] < 0){
26026                 placement = 'right';
26027             }
26028             
26029             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26030                 placement = 'left';
26031             }
26032             
26033             var scroll = Roo.select('body', true).first().getScroll();
26034             
26035             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26036                 placement = 'top';
26037             }
26038             
26039         }
26040         
26041         this.el.alignTo(this.bindEl, align[0],align[1]);
26042         //var arrow = this.el.select('.arrow',true).first();
26043         //arrow.set(align[2], 
26044         
26045         this.el.addClass(placement);
26046         
26047         this.el.addClass('in fade');
26048         
26049         this.hoverState = null;
26050         
26051         if (this.el.hasClass('fade')) {
26052             // fade it?
26053         }
26054         
26055     },
26056     hide : function()
26057     {
26058          
26059         if (!this.el) {
26060             return;
26061         }
26062         //this.el.setXY([0,0]);
26063         this.el.removeClass('in');
26064         //this.el.hide();
26065         
26066     }
26067     
26068 });
26069  
26070
26071  /*
26072  * - LGPL
26073  *
26074  * Location Picker
26075  * 
26076  */
26077
26078 /**
26079  * @class Roo.bootstrap.LocationPicker
26080  * @extends Roo.bootstrap.Component
26081  * Bootstrap LocationPicker class
26082  * @cfg {Number} latitude Position when init default 0
26083  * @cfg {Number} longitude Position when init default 0
26084  * @cfg {Number} zoom default 15
26085  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26086  * @cfg {Boolean} mapTypeControl default false
26087  * @cfg {Boolean} disableDoubleClickZoom default false
26088  * @cfg {Boolean} scrollwheel default true
26089  * @cfg {Boolean} streetViewControl default false
26090  * @cfg {Number} radius default 0
26091  * @cfg {String} locationName
26092  * @cfg {Boolean} draggable default true
26093  * @cfg {Boolean} enableAutocomplete default false
26094  * @cfg {Boolean} enableReverseGeocode default true
26095  * @cfg {String} markerTitle
26096  * 
26097  * @constructor
26098  * Create a new LocationPicker
26099  * @param {Object} config The config object
26100  */
26101
26102
26103 Roo.bootstrap.LocationPicker = function(config){
26104     
26105     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26106     
26107     this.addEvents({
26108         /**
26109          * @event initial
26110          * Fires when the picker initialized.
26111          * @param {Roo.bootstrap.LocationPicker} this
26112          * @param {Google Location} location
26113          */
26114         initial : true,
26115         /**
26116          * @event positionchanged
26117          * Fires when the picker position changed.
26118          * @param {Roo.bootstrap.LocationPicker} this
26119          * @param {Google Location} location
26120          */
26121         positionchanged : true,
26122         /**
26123          * @event resize
26124          * Fires when the map resize.
26125          * @param {Roo.bootstrap.LocationPicker} this
26126          */
26127         resize : true,
26128         /**
26129          * @event show
26130          * Fires when the map show.
26131          * @param {Roo.bootstrap.LocationPicker} this
26132          */
26133         show : true,
26134         /**
26135          * @event hide
26136          * Fires when the map hide.
26137          * @param {Roo.bootstrap.LocationPicker} this
26138          */
26139         hide : true,
26140         /**
26141          * @event mapClick
26142          * Fires when click the map.
26143          * @param {Roo.bootstrap.LocationPicker} this
26144          * @param {Map event} e
26145          */
26146         mapClick : true,
26147         /**
26148          * @event mapRightClick
26149          * Fires when right click the map.
26150          * @param {Roo.bootstrap.LocationPicker} this
26151          * @param {Map event} e
26152          */
26153         mapRightClick : true,
26154         /**
26155          * @event markerClick
26156          * Fires when click the marker.
26157          * @param {Roo.bootstrap.LocationPicker} this
26158          * @param {Map event} e
26159          */
26160         markerClick : true,
26161         /**
26162          * @event markerRightClick
26163          * Fires when right click the marker.
26164          * @param {Roo.bootstrap.LocationPicker} this
26165          * @param {Map event} e
26166          */
26167         markerRightClick : true,
26168         /**
26169          * @event OverlayViewDraw
26170          * Fires when OverlayView Draw
26171          * @param {Roo.bootstrap.LocationPicker} this
26172          */
26173         OverlayViewDraw : true,
26174         /**
26175          * @event OverlayViewOnAdd
26176          * Fires when OverlayView Draw
26177          * @param {Roo.bootstrap.LocationPicker} this
26178          */
26179         OverlayViewOnAdd : true,
26180         /**
26181          * @event OverlayViewOnRemove
26182          * Fires when OverlayView Draw
26183          * @param {Roo.bootstrap.LocationPicker} this
26184          */
26185         OverlayViewOnRemove : true,
26186         /**
26187          * @event OverlayViewShow
26188          * Fires when OverlayView Draw
26189          * @param {Roo.bootstrap.LocationPicker} this
26190          * @param {Pixel} cpx
26191          */
26192         OverlayViewShow : true,
26193         /**
26194          * @event OverlayViewHide
26195          * Fires when OverlayView Draw
26196          * @param {Roo.bootstrap.LocationPicker} this
26197          */
26198         OverlayViewHide : true,
26199         /**
26200          * @event loadexception
26201          * Fires when load google lib failed.
26202          * @param {Roo.bootstrap.LocationPicker} this
26203          */
26204         loadexception : true
26205     });
26206         
26207 };
26208
26209 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26210     
26211     gMapContext: false,
26212     
26213     latitude: 0,
26214     longitude: 0,
26215     zoom: 15,
26216     mapTypeId: false,
26217     mapTypeControl: false,
26218     disableDoubleClickZoom: false,
26219     scrollwheel: true,
26220     streetViewControl: false,
26221     radius: 0,
26222     locationName: '',
26223     draggable: true,
26224     enableAutocomplete: false,
26225     enableReverseGeocode: true,
26226     markerTitle: '',
26227     
26228     getAutoCreate: function()
26229     {
26230
26231         var cfg = {
26232             tag: 'div',
26233             cls: 'roo-location-picker'
26234         };
26235         
26236         return cfg
26237     },
26238     
26239     initEvents: function(ct, position)
26240     {       
26241         if(!this.el.getWidth() || this.isApplied()){
26242             return;
26243         }
26244         
26245         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26246         
26247         this.initial();
26248     },
26249     
26250     initial: function()
26251     {
26252         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26253             this.fireEvent('loadexception', this);
26254             return;
26255         }
26256         
26257         if(!this.mapTypeId){
26258             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26259         }
26260         
26261         this.gMapContext = this.GMapContext();
26262         
26263         this.initOverlayView();
26264         
26265         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26266         
26267         var _this = this;
26268                 
26269         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26270             _this.setPosition(_this.gMapContext.marker.position);
26271         });
26272         
26273         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26274             _this.fireEvent('mapClick', this, event);
26275             
26276         });
26277
26278         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26279             _this.fireEvent('mapRightClick', this, event);
26280             
26281         });
26282         
26283         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26284             _this.fireEvent('markerClick', this, event);
26285             
26286         });
26287
26288         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26289             _this.fireEvent('markerRightClick', this, event);
26290             
26291         });
26292         
26293         this.setPosition(this.gMapContext.location);
26294         
26295         this.fireEvent('initial', this, this.gMapContext.location);
26296     },
26297     
26298     initOverlayView: function()
26299     {
26300         var _this = this;
26301         
26302         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26303             
26304             draw: function()
26305             {
26306                 _this.fireEvent('OverlayViewDraw', _this);
26307             },
26308             
26309             onAdd: function()
26310             {
26311                 _this.fireEvent('OverlayViewOnAdd', _this);
26312             },
26313             
26314             onRemove: function()
26315             {
26316                 _this.fireEvent('OverlayViewOnRemove', _this);
26317             },
26318             
26319             show: function(cpx)
26320             {
26321                 _this.fireEvent('OverlayViewShow', _this, cpx);
26322             },
26323             
26324             hide: function()
26325             {
26326                 _this.fireEvent('OverlayViewHide', _this);
26327             }
26328             
26329         });
26330     },
26331     
26332     fromLatLngToContainerPixel: function(event)
26333     {
26334         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26335     },
26336     
26337     isApplied: function() 
26338     {
26339         return this.getGmapContext() == false ? false : true;
26340     },
26341     
26342     getGmapContext: function() 
26343     {
26344         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26345     },
26346     
26347     GMapContext: function() 
26348     {
26349         var position = new google.maps.LatLng(this.latitude, this.longitude);
26350         
26351         var _map = new google.maps.Map(this.el.dom, {
26352             center: position,
26353             zoom: this.zoom,
26354             mapTypeId: this.mapTypeId,
26355             mapTypeControl: this.mapTypeControl,
26356             disableDoubleClickZoom: this.disableDoubleClickZoom,
26357             scrollwheel: this.scrollwheel,
26358             streetViewControl: this.streetViewControl,
26359             locationName: this.locationName,
26360             draggable: this.draggable,
26361             enableAutocomplete: this.enableAutocomplete,
26362             enableReverseGeocode: this.enableReverseGeocode
26363         });
26364         
26365         var _marker = new google.maps.Marker({
26366             position: position,
26367             map: _map,
26368             title: this.markerTitle,
26369             draggable: this.draggable
26370         });
26371         
26372         return {
26373             map: _map,
26374             marker: _marker,
26375             circle: null,
26376             location: position,
26377             radius: this.radius,
26378             locationName: this.locationName,
26379             addressComponents: {
26380                 formatted_address: null,
26381                 addressLine1: null,
26382                 addressLine2: null,
26383                 streetName: null,
26384                 streetNumber: null,
26385                 city: null,
26386                 district: null,
26387                 state: null,
26388                 stateOrProvince: null
26389             },
26390             settings: this,
26391             domContainer: this.el.dom,
26392             geodecoder: new google.maps.Geocoder()
26393         };
26394     },
26395     
26396     drawCircle: function(center, radius, options) 
26397     {
26398         if (this.gMapContext.circle != null) {
26399             this.gMapContext.circle.setMap(null);
26400         }
26401         if (radius > 0) {
26402             radius *= 1;
26403             options = Roo.apply({}, options, {
26404                 strokeColor: "#0000FF",
26405                 strokeOpacity: .35,
26406                 strokeWeight: 2,
26407                 fillColor: "#0000FF",
26408                 fillOpacity: .2
26409             });
26410             
26411             options.map = this.gMapContext.map;
26412             options.radius = radius;
26413             options.center = center;
26414             this.gMapContext.circle = new google.maps.Circle(options);
26415             return this.gMapContext.circle;
26416         }
26417         
26418         return null;
26419     },
26420     
26421     setPosition: function(location) 
26422     {
26423         this.gMapContext.location = location;
26424         this.gMapContext.marker.setPosition(location);
26425         this.gMapContext.map.panTo(location);
26426         this.drawCircle(location, this.gMapContext.radius, {});
26427         
26428         var _this = this;
26429         
26430         if (this.gMapContext.settings.enableReverseGeocode) {
26431             this.gMapContext.geodecoder.geocode({
26432                 latLng: this.gMapContext.location
26433             }, function(results, status) {
26434                 
26435                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26436                     _this.gMapContext.locationName = results[0].formatted_address;
26437                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26438                     
26439                     _this.fireEvent('positionchanged', this, location);
26440                 }
26441             });
26442             
26443             return;
26444         }
26445         
26446         this.fireEvent('positionchanged', this, location);
26447     },
26448     
26449     resize: function()
26450     {
26451         google.maps.event.trigger(this.gMapContext.map, "resize");
26452         
26453         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26454         
26455         this.fireEvent('resize', this);
26456     },
26457     
26458     setPositionByLatLng: function(latitude, longitude)
26459     {
26460         this.setPosition(new google.maps.LatLng(latitude, longitude));
26461     },
26462     
26463     getCurrentPosition: function() 
26464     {
26465         return {
26466             latitude: this.gMapContext.location.lat(),
26467             longitude: this.gMapContext.location.lng()
26468         };
26469     },
26470     
26471     getAddressName: function() 
26472     {
26473         return this.gMapContext.locationName;
26474     },
26475     
26476     getAddressComponents: function() 
26477     {
26478         return this.gMapContext.addressComponents;
26479     },
26480     
26481     address_component_from_google_geocode: function(address_components) 
26482     {
26483         var result = {};
26484         
26485         for (var i = 0; i < address_components.length; i++) {
26486             var component = address_components[i];
26487             if (component.types.indexOf("postal_code") >= 0) {
26488                 result.postalCode = component.short_name;
26489             } else if (component.types.indexOf("street_number") >= 0) {
26490                 result.streetNumber = component.short_name;
26491             } else if (component.types.indexOf("route") >= 0) {
26492                 result.streetName = component.short_name;
26493             } else if (component.types.indexOf("neighborhood") >= 0) {
26494                 result.city = component.short_name;
26495             } else if (component.types.indexOf("locality") >= 0) {
26496                 result.city = component.short_name;
26497             } else if (component.types.indexOf("sublocality") >= 0) {
26498                 result.district = component.short_name;
26499             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26500                 result.stateOrProvince = component.short_name;
26501             } else if (component.types.indexOf("country") >= 0) {
26502                 result.country = component.short_name;
26503             }
26504         }
26505         
26506         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26507         result.addressLine2 = "";
26508         return result;
26509     },
26510     
26511     setZoomLevel: function(zoom)
26512     {
26513         this.gMapContext.map.setZoom(zoom);
26514     },
26515     
26516     show: function()
26517     {
26518         if(!this.el){
26519             return;
26520         }
26521         
26522         this.el.show();
26523         
26524         this.resize();
26525         
26526         this.fireEvent('show', this);
26527     },
26528     
26529     hide: function()
26530     {
26531         if(!this.el){
26532             return;
26533         }
26534         
26535         this.el.hide();
26536         
26537         this.fireEvent('hide', this);
26538     }
26539     
26540 });
26541
26542 Roo.apply(Roo.bootstrap.LocationPicker, {
26543     
26544     OverlayView : function(map, options)
26545     {
26546         options = options || {};
26547         
26548         this.setMap(map);
26549     }
26550     
26551     
26552 });/*
26553  * - LGPL
26554  *
26555  * Alert
26556  * 
26557  */
26558
26559 /**
26560  * @class Roo.bootstrap.Alert
26561  * @extends Roo.bootstrap.Component
26562  * Bootstrap Alert class
26563  * @cfg {String} title The title of alert
26564  * @cfg {String} html The content of alert
26565  * @cfg {String} weight (  success | info | warning | danger )
26566  * @cfg {String} faicon font-awesomeicon
26567  * 
26568  * @constructor
26569  * Create a new alert
26570  * @param {Object} config The config object
26571  */
26572
26573
26574 Roo.bootstrap.Alert = function(config){
26575     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26576     
26577 };
26578
26579 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26580     
26581     title: '',
26582     html: '',
26583     weight: false,
26584     faicon: false,
26585     
26586     getAutoCreate : function()
26587     {
26588         
26589         var cfg = {
26590             tag : 'div',
26591             cls : 'alert',
26592             cn : [
26593                 {
26594                     tag : 'i',
26595                     cls : 'roo-alert-icon'
26596                     
26597                 },
26598                 {
26599                     tag : 'b',
26600                     cls : 'roo-alert-title',
26601                     html : this.title
26602                 },
26603                 {
26604                     tag : 'span',
26605                     cls : 'roo-alert-text',
26606                     html : this.html
26607                 }
26608             ]
26609         };
26610         
26611         if(this.faicon){
26612             cfg.cn[0].cls += ' fa ' + this.faicon;
26613         }
26614         
26615         if(this.weight){
26616             cfg.cls += ' alert-' + this.weight;
26617         }
26618         
26619         return cfg;
26620     },
26621     
26622     initEvents: function() 
26623     {
26624         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26625     },
26626     
26627     setTitle : function(str)
26628     {
26629         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26630     },
26631     
26632     setText : function(str)
26633     {
26634         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26635     },
26636     
26637     setWeight : function(weight)
26638     {
26639         if(this.weight){
26640             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26641         }
26642         
26643         this.weight = weight;
26644         
26645         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26646     },
26647     
26648     setIcon : function(icon)
26649     {
26650         if(this.faicon){
26651             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26652         }
26653         
26654         this.faicon = icon;
26655         
26656         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26657     },
26658     
26659     hide: function() 
26660     {
26661         this.el.hide();   
26662     },
26663     
26664     show: function() 
26665     {  
26666         this.el.show();   
26667     }
26668     
26669 });
26670
26671  
26672 /*
26673 * Licence: LGPL
26674 */
26675
26676 /**
26677  * @class Roo.bootstrap.UploadCropbox
26678  * @extends Roo.bootstrap.Component
26679  * Bootstrap UploadCropbox class
26680  * @cfg {String} emptyText show when image has been loaded
26681  * @cfg {String} rotateNotify show when image too small to rotate
26682  * @cfg {Number} errorTimeout default 3000
26683  * @cfg {Number} minWidth default 300
26684  * @cfg {Number} minHeight default 300
26685  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26686  * @cfg {Boolean} isDocument (true|false) default false
26687  * @cfg {String} url action url
26688  * @cfg {String} paramName default 'imageUpload'
26689  * @cfg {String} method default POST
26690  * @cfg {Boolean} loadMask (true|false) default true
26691  * @cfg {Boolean} loadingText default 'Loading...'
26692  * 
26693  * @constructor
26694  * Create a new UploadCropbox
26695  * @param {Object} config The config object
26696  */
26697
26698 Roo.bootstrap.UploadCropbox = function(config){
26699     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26700     
26701     this.addEvents({
26702         /**
26703          * @event beforeselectfile
26704          * Fire before select file
26705          * @param {Roo.bootstrap.UploadCropbox} this
26706          */
26707         "beforeselectfile" : true,
26708         /**
26709          * @event initial
26710          * Fire after initEvent
26711          * @param {Roo.bootstrap.UploadCropbox} this
26712          */
26713         "initial" : true,
26714         /**
26715          * @event crop
26716          * Fire after initEvent
26717          * @param {Roo.bootstrap.UploadCropbox} this
26718          * @param {String} data
26719          */
26720         "crop" : true,
26721         /**
26722          * @event prepare
26723          * Fire when preparing the file data
26724          * @param {Roo.bootstrap.UploadCropbox} this
26725          * @param {Object} file
26726          */
26727         "prepare" : true,
26728         /**
26729          * @event exception
26730          * Fire when get exception
26731          * @param {Roo.bootstrap.UploadCropbox} this
26732          * @param {XMLHttpRequest} xhr
26733          */
26734         "exception" : true,
26735         /**
26736          * @event beforeloadcanvas
26737          * Fire before load the canvas
26738          * @param {Roo.bootstrap.UploadCropbox} this
26739          * @param {String} src
26740          */
26741         "beforeloadcanvas" : true,
26742         /**
26743          * @event trash
26744          * Fire when trash image
26745          * @param {Roo.bootstrap.UploadCropbox} this
26746          */
26747         "trash" : true,
26748         /**
26749          * @event download
26750          * Fire when download the image
26751          * @param {Roo.bootstrap.UploadCropbox} this
26752          */
26753         "download" : true,
26754         /**
26755          * @event footerbuttonclick
26756          * Fire when footerbuttonclick
26757          * @param {Roo.bootstrap.UploadCropbox} this
26758          * @param {String} type
26759          */
26760         "footerbuttonclick" : true,
26761         /**
26762          * @event resize
26763          * Fire when resize
26764          * @param {Roo.bootstrap.UploadCropbox} this
26765          */
26766         "resize" : true,
26767         /**
26768          * @event rotate
26769          * Fire when rotate the image
26770          * @param {Roo.bootstrap.UploadCropbox} this
26771          * @param {String} pos
26772          */
26773         "rotate" : true,
26774         /**
26775          * @event inspect
26776          * Fire when inspect the file
26777          * @param {Roo.bootstrap.UploadCropbox} this
26778          * @param {Object} file
26779          */
26780         "inspect" : true,
26781         /**
26782          * @event upload
26783          * Fire when xhr upload the file
26784          * @param {Roo.bootstrap.UploadCropbox} this
26785          * @param {Object} data
26786          */
26787         "upload" : true,
26788         /**
26789          * @event arrange
26790          * Fire when arrange the file data
26791          * @param {Roo.bootstrap.UploadCropbox} this
26792          * @param {Object} formData
26793          */
26794         "arrange" : true
26795     });
26796     
26797     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26798 };
26799
26800 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26801     
26802     emptyText : 'Click to upload image',
26803     rotateNotify : 'Image is too small to rotate',
26804     errorTimeout : 3000,
26805     scale : 0,
26806     baseScale : 1,
26807     rotate : 0,
26808     dragable : false,
26809     pinching : false,
26810     mouseX : 0,
26811     mouseY : 0,
26812     cropData : false,
26813     minWidth : 300,
26814     minHeight : 300,
26815     file : false,
26816     exif : {},
26817     baseRotate : 1,
26818     cropType : 'image/jpeg',
26819     buttons : false,
26820     canvasLoaded : false,
26821     isDocument : false,
26822     method : 'POST',
26823     paramName : 'imageUpload',
26824     loadMask : true,
26825     loadingText : 'Loading...',
26826     maskEl : false,
26827     
26828     getAutoCreate : function()
26829     {
26830         var cfg = {
26831             tag : 'div',
26832             cls : 'roo-upload-cropbox',
26833             cn : [
26834                 {
26835                     tag : 'input',
26836                     cls : 'roo-upload-cropbox-selector',
26837                     type : 'file'
26838                 },
26839                 {
26840                     tag : 'div',
26841                     cls : 'roo-upload-cropbox-body',
26842                     style : 'cursor:pointer',
26843                     cn : [
26844                         {
26845                             tag : 'div',
26846                             cls : 'roo-upload-cropbox-preview'
26847                         },
26848                         {
26849                             tag : 'div',
26850                             cls : 'roo-upload-cropbox-thumb'
26851                         },
26852                         {
26853                             tag : 'div',
26854                             cls : 'roo-upload-cropbox-empty-notify',
26855                             html : this.emptyText
26856                         },
26857                         {
26858                             tag : 'div',
26859                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26860                             html : this.rotateNotify
26861                         }
26862                     ]
26863                 },
26864                 {
26865                     tag : 'div',
26866                     cls : 'roo-upload-cropbox-footer',
26867                     cn : {
26868                         tag : 'div',
26869                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26870                         cn : []
26871                     }
26872                 }
26873             ]
26874         };
26875         
26876         return cfg;
26877     },
26878     
26879     onRender : function(ct, position)
26880     {
26881         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26882         
26883         if (this.buttons.length) {
26884             
26885             Roo.each(this.buttons, function(bb) {
26886                 
26887                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26888                 
26889                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26890                 
26891             }, this);
26892         }
26893         
26894         if(this.loadMask){
26895             this.maskEl = this.el;
26896         }
26897     },
26898     
26899     initEvents : function()
26900     {
26901         this.urlAPI = (window.createObjectURL && window) || 
26902                                 (window.URL && URL.revokeObjectURL && URL) || 
26903                                 (window.webkitURL && webkitURL);
26904                         
26905         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26906         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26907         
26908         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26909         this.selectorEl.hide();
26910         
26911         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26912         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26913         
26914         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26915         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26916         this.thumbEl.hide();
26917         
26918         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26919         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26920         
26921         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26922         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26923         this.errorEl.hide();
26924         
26925         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26926         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26927         this.footerEl.hide();
26928         
26929         this.setThumbBoxSize();
26930         
26931         this.bind();
26932         
26933         this.resize();
26934         
26935         this.fireEvent('initial', this);
26936     },
26937
26938     bind : function()
26939     {
26940         var _this = this;
26941         
26942         window.addEventListener("resize", function() { _this.resize(); } );
26943         
26944         this.bodyEl.on('click', this.beforeSelectFile, this);
26945         
26946         if(Roo.isTouch){
26947             this.bodyEl.on('touchstart', this.onTouchStart, this);
26948             this.bodyEl.on('touchmove', this.onTouchMove, this);
26949             this.bodyEl.on('touchend', this.onTouchEnd, this);
26950         }
26951         
26952         if(!Roo.isTouch){
26953             this.bodyEl.on('mousedown', this.onMouseDown, this);
26954             this.bodyEl.on('mousemove', this.onMouseMove, this);
26955             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26956             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26957             Roo.get(document).on('mouseup', this.onMouseUp, this);
26958         }
26959         
26960         this.selectorEl.on('change', this.onFileSelected, this);
26961     },
26962     
26963     reset : function()
26964     {    
26965         this.scale = 0;
26966         this.baseScale = 1;
26967         this.rotate = 0;
26968         this.baseRotate = 1;
26969         this.dragable = false;
26970         this.pinching = false;
26971         this.mouseX = 0;
26972         this.mouseY = 0;
26973         this.cropData = false;
26974         this.notifyEl.dom.innerHTML = this.emptyText;
26975         
26976         this.selectorEl.dom.value = '';
26977         
26978     },
26979     
26980     resize : function()
26981     {
26982         if(this.fireEvent('resize', this) != false){
26983             this.setThumbBoxPosition();
26984             this.setCanvasPosition();
26985         }
26986     },
26987     
26988     onFooterButtonClick : function(e, el, o, type)
26989     {
26990         switch (type) {
26991             case 'rotate-left' :
26992                 this.onRotateLeft(e);
26993                 break;
26994             case 'rotate-right' :
26995                 this.onRotateRight(e);
26996                 break;
26997             case 'picture' :
26998                 this.beforeSelectFile(e);
26999                 break;
27000             case 'trash' :
27001                 this.trash(e);
27002                 break;
27003             case 'crop' :
27004                 this.crop(e);
27005                 break;
27006             case 'download' :
27007                 this.download(e);
27008                 break;
27009             default :
27010                 break;
27011         }
27012         
27013         this.fireEvent('footerbuttonclick', this, type);
27014     },
27015     
27016     beforeSelectFile : function(e)
27017     {
27018         e.preventDefault();
27019         
27020         if(this.fireEvent('beforeselectfile', this) != false){
27021             this.selectorEl.dom.click();
27022         }
27023     },
27024     
27025     onFileSelected : function(e)
27026     {
27027         e.preventDefault();
27028         
27029         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27030             return;
27031         }
27032         
27033         var file = this.selectorEl.dom.files[0];
27034         
27035         if(this.fireEvent('inspect', this, file) != false){
27036             this.prepare(file);
27037         }
27038         
27039     },
27040     
27041     trash : function(e)
27042     {
27043         this.fireEvent('trash', this);
27044     },
27045     
27046     download : function(e)
27047     {
27048         this.fireEvent('download', this);
27049     },
27050     
27051     loadCanvas : function(src)
27052     {   
27053         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27054             
27055             this.reset();
27056             
27057             this.imageEl = document.createElement('img');
27058             
27059             var _this = this;
27060             
27061             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27062             
27063             this.imageEl.src = src;
27064         }
27065     },
27066     
27067     onLoadCanvas : function()
27068     {   
27069         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27070         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27071         
27072         this.bodyEl.un('click', this.beforeSelectFile, this);
27073         
27074         this.notifyEl.hide();
27075         this.thumbEl.show();
27076         this.footerEl.show();
27077         
27078         this.baseRotateLevel();
27079         
27080         if(this.isDocument){
27081             this.setThumbBoxSize();
27082         }
27083         
27084         this.setThumbBoxPosition();
27085         
27086         this.baseScaleLevel();
27087         
27088         this.draw();
27089         
27090         this.resize();
27091         
27092         this.canvasLoaded = true;
27093         
27094         if(this.loadMask){
27095             this.maskEl.unmask();
27096         }
27097         
27098     },
27099     
27100     setCanvasPosition : function()
27101     {   
27102         if(!this.canvasEl){
27103             return;
27104         }
27105         
27106         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27107         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27108         
27109         this.previewEl.setLeft(pw);
27110         this.previewEl.setTop(ph);
27111         
27112     },
27113     
27114     onMouseDown : function(e)
27115     {   
27116         e.stopEvent();
27117         
27118         this.dragable = true;
27119         this.pinching = false;
27120         
27121         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27122             this.dragable = false;
27123             return;
27124         }
27125         
27126         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27127         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27128         
27129     },
27130     
27131     onMouseMove : function(e)
27132     {   
27133         e.stopEvent();
27134         
27135         if(!this.canvasLoaded){
27136             return;
27137         }
27138         
27139         if (!this.dragable){
27140             return;
27141         }
27142         
27143         var minX = Math.ceil(this.thumbEl.getLeft(true));
27144         var minY = Math.ceil(this.thumbEl.getTop(true));
27145         
27146         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27147         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27148         
27149         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27150         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27151         
27152         x = x - this.mouseX;
27153         y = y - this.mouseY;
27154         
27155         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27156         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27157         
27158         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27159         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27160         
27161         this.previewEl.setLeft(bgX);
27162         this.previewEl.setTop(bgY);
27163         
27164         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27165         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27166     },
27167     
27168     onMouseUp : function(e)
27169     {   
27170         e.stopEvent();
27171         
27172         this.dragable = false;
27173     },
27174     
27175     onMouseWheel : function(e)
27176     {   
27177         e.stopEvent();
27178         
27179         this.startScale = this.scale;
27180         
27181         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27182         
27183         if(!this.zoomable()){
27184             this.scale = this.startScale;
27185             return;
27186         }
27187         
27188         this.draw();
27189         
27190         return;
27191     },
27192     
27193     zoomable : function()
27194     {
27195         var minScale = this.thumbEl.getWidth() / this.minWidth;
27196         
27197         if(this.minWidth < this.minHeight){
27198             minScale = this.thumbEl.getHeight() / this.minHeight;
27199         }
27200         
27201         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27202         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27203         
27204         if(
27205                 this.isDocument &&
27206                 (this.rotate == 0 || this.rotate == 180) && 
27207                 (
27208                     width > this.imageEl.OriginWidth || 
27209                     height > this.imageEl.OriginHeight ||
27210                     (width < this.minWidth && height < this.minHeight)
27211                 )
27212         ){
27213             return false;
27214         }
27215         
27216         if(
27217                 this.isDocument &&
27218                 (this.rotate == 90 || this.rotate == 270) && 
27219                 (
27220                     width > this.imageEl.OriginWidth || 
27221                     height > this.imageEl.OriginHeight ||
27222                     (width < this.minHeight && height < this.minWidth)
27223                 )
27224         ){
27225             return false;
27226         }
27227         
27228         if(
27229                 !this.isDocument &&
27230                 (this.rotate == 0 || this.rotate == 180) && 
27231                 (
27232                     width < this.minWidth || 
27233                     width > this.imageEl.OriginWidth || 
27234                     height < this.minHeight || 
27235                     height > this.imageEl.OriginHeight
27236                 )
27237         ){
27238             return false;
27239         }
27240         
27241         if(
27242                 !this.isDocument &&
27243                 (this.rotate == 90 || this.rotate == 270) && 
27244                 (
27245                     width < this.minHeight || 
27246                     width > this.imageEl.OriginWidth || 
27247                     height < this.minWidth || 
27248                     height > this.imageEl.OriginHeight
27249                 )
27250         ){
27251             return false;
27252         }
27253         
27254         return true;
27255         
27256     },
27257     
27258     onRotateLeft : function(e)
27259     {   
27260         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27261             
27262             var minScale = this.thumbEl.getWidth() / this.minWidth;
27263             
27264             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27265             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27266             
27267             this.startScale = this.scale;
27268             
27269             while (this.getScaleLevel() < minScale){
27270             
27271                 this.scale = this.scale + 1;
27272                 
27273                 if(!this.zoomable()){
27274                     break;
27275                 }
27276                 
27277                 if(
27278                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27279                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27280                 ){
27281                     continue;
27282                 }
27283                 
27284                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27285
27286                 this.draw();
27287                 
27288                 return;
27289             }
27290             
27291             this.scale = this.startScale;
27292             
27293             this.onRotateFail();
27294             
27295             return false;
27296         }
27297         
27298         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27299
27300         if(this.isDocument){
27301             this.setThumbBoxSize();
27302             this.setThumbBoxPosition();
27303             this.setCanvasPosition();
27304         }
27305         
27306         this.draw();
27307         
27308         this.fireEvent('rotate', this, 'left');
27309         
27310     },
27311     
27312     onRotateRight : function(e)
27313     {
27314         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27315             
27316             var minScale = this.thumbEl.getWidth() / this.minWidth;
27317         
27318             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27319             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27320             
27321             this.startScale = this.scale;
27322             
27323             while (this.getScaleLevel() < minScale){
27324             
27325                 this.scale = this.scale + 1;
27326                 
27327                 if(!this.zoomable()){
27328                     break;
27329                 }
27330                 
27331                 if(
27332                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27333                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27334                 ){
27335                     continue;
27336                 }
27337                 
27338                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27339
27340                 this.draw();
27341                 
27342                 return;
27343             }
27344             
27345             this.scale = this.startScale;
27346             
27347             this.onRotateFail();
27348             
27349             return false;
27350         }
27351         
27352         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27353
27354         if(this.isDocument){
27355             this.setThumbBoxSize();
27356             this.setThumbBoxPosition();
27357             this.setCanvasPosition();
27358         }
27359         
27360         this.draw();
27361         
27362         this.fireEvent('rotate', this, 'right');
27363     },
27364     
27365     onRotateFail : function()
27366     {
27367         this.errorEl.show(true);
27368         
27369         var _this = this;
27370         
27371         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27372     },
27373     
27374     draw : function()
27375     {
27376         this.previewEl.dom.innerHTML = '';
27377         
27378         var canvasEl = document.createElement("canvas");
27379         
27380         var contextEl = canvasEl.getContext("2d");
27381         
27382         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27383         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27384         var center = this.imageEl.OriginWidth / 2;
27385         
27386         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27387             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27388             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27389             center = this.imageEl.OriginHeight / 2;
27390         }
27391         
27392         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27393         
27394         contextEl.translate(center, center);
27395         contextEl.rotate(this.rotate * Math.PI / 180);
27396
27397         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27398         
27399         this.canvasEl = document.createElement("canvas");
27400         
27401         this.contextEl = this.canvasEl.getContext("2d");
27402         
27403         switch (this.rotate) {
27404             case 0 :
27405                 
27406                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27407                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27408                 
27409                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27410                 
27411                 break;
27412             case 90 : 
27413                 
27414                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27415                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27416                 
27417                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27418                     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);
27419                     break;
27420                 }
27421                 
27422                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27423                 
27424                 break;
27425             case 180 :
27426                 
27427                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27428                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27429                 
27430                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27431                     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);
27432                     break;
27433                 }
27434                 
27435                 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);
27436                 
27437                 break;
27438             case 270 :
27439                 
27440                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27441                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27442         
27443                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27444                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27445                     break;
27446                 }
27447                 
27448                 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);
27449                 
27450                 break;
27451             default : 
27452                 break;
27453         }
27454         
27455         this.previewEl.appendChild(this.canvasEl);
27456         
27457         this.setCanvasPosition();
27458     },
27459     
27460     crop : function()
27461     {
27462         if(!this.canvasLoaded){
27463             return;
27464         }
27465         
27466         var imageCanvas = document.createElement("canvas");
27467         
27468         var imageContext = imageCanvas.getContext("2d");
27469         
27470         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27471         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27472         
27473         var center = imageCanvas.width / 2;
27474         
27475         imageContext.translate(center, center);
27476         
27477         imageContext.rotate(this.rotate * Math.PI / 180);
27478         
27479         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27480         
27481         var canvas = document.createElement("canvas");
27482         
27483         var context = canvas.getContext("2d");
27484                 
27485         canvas.width = this.minWidth;
27486         canvas.height = this.minHeight;
27487
27488         switch (this.rotate) {
27489             case 0 :
27490                 
27491                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27492                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27493                 
27494                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27495                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27496                 
27497                 var targetWidth = this.minWidth - 2 * x;
27498                 var targetHeight = this.minHeight - 2 * y;
27499                 
27500                 var scale = 1;
27501                 
27502                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27503                     scale = targetWidth / width;
27504                 }
27505                 
27506                 if(x > 0 && y == 0){
27507                     scale = targetHeight / height;
27508                 }
27509                 
27510                 if(x > 0 && y > 0){
27511                     scale = targetWidth / width;
27512                     
27513                     if(width < height){
27514                         scale = targetHeight / height;
27515                     }
27516                 }
27517                 
27518                 context.scale(scale, scale);
27519                 
27520                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27521                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27522
27523                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27524                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27525
27526                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27527                 
27528                 break;
27529             case 90 : 
27530                 
27531                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27532                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27533                 
27534                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27535                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27536                 
27537                 var targetWidth = this.minWidth - 2 * x;
27538                 var targetHeight = this.minHeight - 2 * y;
27539                 
27540                 var scale = 1;
27541                 
27542                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27543                     scale = targetWidth / width;
27544                 }
27545                 
27546                 if(x > 0 && y == 0){
27547                     scale = targetHeight / height;
27548                 }
27549                 
27550                 if(x > 0 && y > 0){
27551                     scale = targetWidth / width;
27552                     
27553                     if(width < height){
27554                         scale = targetHeight / height;
27555                     }
27556                 }
27557                 
27558                 context.scale(scale, scale);
27559                 
27560                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27561                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27562
27563                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27564                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27565                 
27566                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27567                 
27568                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27569                 
27570                 break;
27571             case 180 :
27572                 
27573                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27574                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27575                 
27576                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27577                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27578                 
27579                 var targetWidth = this.minWidth - 2 * x;
27580                 var targetHeight = this.minHeight - 2 * y;
27581                 
27582                 var scale = 1;
27583                 
27584                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27585                     scale = targetWidth / width;
27586                 }
27587                 
27588                 if(x > 0 && y == 0){
27589                     scale = targetHeight / height;
27590                 }
27591                 
27592                 if(x > 0 && y > 0){
27593                     scale = targetWidth / width;
27594                     
27595                     if(width < height){
27596                         scale = targetHeight / height;
27597                     }
27598                 }
27599                 
27600                 context.scale(scale, scale);
27601                 
27602                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27603                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27604
27605                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27606                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27607
27608                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27609                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27610                 
27611                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27612                 
27613                 break;
27614             case 270 :
27615                 
27616                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27617                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27618                 
27619                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27620                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27621                 
27622                 var targetWidth = this.minWidth - 2 * x;
27623                 var targetHeight = this.minHeight - 2 * y;
27624                 
27625                 var scale = 1;
27626                 
27627                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27628                     scale = targetWidth / width;
27629                 }
27630                 
27631                 if(x > 0 && y == 0){
27632                     scale = targetHeight / height;
27633                 }
27634                 
27635                 if(x > 0 && y > 0){
27636                     scale = targetWidth / width;
27637                     
27638                     if(width < height){
27639                         scale = targetHeight / height;
27640                     }
27641                 }
27642                 
27643                 context.scale(scale, scale);
27644                 
27645                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27646                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27647
27648                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27649                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27650                 
27651                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27652                 
27653                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27654                 
27655                 break;
27656             default : 
27657                 break;
27658         }
27659         
27660         this.cropData = canvas.toDataURL(this.cropType);
27661         
27662         if(this.fireEvent('crop', this, this.cropData) !== false){
27663             this.process(this.file, this.cropData);
27664         }
27665         
27666         return;
27667         
27668     },
27669     
27670     setThumbBoxSize : function()
27671     {
27672         var width, height;
27673         
27674         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27675             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27676             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27677             
27678             this.minWidth = width;
27679             this.minHeight = height;
27680             
27681             if(this.rotate == 90 || this.rotate == 270){
27682                 this.minWidth = height;
27683                 this.minHeight = width;
27684             }
27685         }
27686         
27687         height = 300;
27688         width = Math.ceil(this.minWidth * height / this.minHeight);
27689         
27690         if(this.minWidth > this.minHeight){
27691             width = 300;
27692             height = Math.ceil(this.minHeight * width / this.minWidth);
27693         }
27694         
27695         this.thumbEl.setStyle({
27696             width : width + 'px',
27697             height : height + 'px'
27698         });
27699
27700         return;
27701             
27702     },
27703     
27704     setThumbBoxPosition : function()
27705     {
27706         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27707         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27708         
27709         this.thumbEl.setLeft(x);
27710         this.thumbEl.setTop(y);
27711         
27712     },
27713     
27714     baseRotateLevel : function()
27715     {
27716         this.baseRotate = 1;
27717         
27718         if(
27719                 typeof(this.exif) != 'undefined' &&
27720                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27721                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27722         ){
27723             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27724         }
27725         
27726         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27727         
27728     },
27729     
27730     baseScaleLevel : function()
27731     {
27732         var width, height;
27733         
27734         if(this.isDocument){
27735             
27736             if(this.baseRotate == 6 || this.baseRotate == 8){
27737             
27738                 height = this.thumbEl.getHeight();
27739                 this.baseScale = height / this.imageEl.OriginWidth;
27740
27741                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27742                     width = this.thumbEl.getWidth();
27743                     this.baseScale = width / this.imageEl.OriginHeight;
27744                 }
27745
27746                 return;
27747             }
27748
27749             height = this.thumbEl.getHeight();
27750             this.baseScale = height / this.imageEl.OriginHeight;
27751
27752             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27753                 width = this.thumbEl.getWidth();
27754                 this.baseScale = width / this.imageEl.OriginWidth;
27755             }
27756
27757             return;
27758         }
27759         
27760         if(this.baseRotate == 6 || this.baseRotate == 8){
27761             
27762             width = this.thumbEl.getHeight();
27763             this.baseScale = width / this.imageEl.OriginHeight;
27764             
27765             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27766                 height = this.thumbEl.getWidth();
27767                 this.baseScale = height / this.imageEl.OriginHeight;
27768             }
27769             
27770             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27771                 height = this.thumbEl.getWidth();
27772                 this.baseScale = height / this.imageEl.OriginHeight;
27773                 
27774                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27775                     width = this.thumbEl.getHeight();
27776                     this.baseScale = width / this.imageEl.OriginWidth;
27777                 }
27778             }
27779             
27780             return;
27781         }
27782         
27783         width = this.thumbEl.getWidth();
27784         this.baseScale = width / this.imageEl.OriginWidth;
27785         
27786         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27787             height = this.thumbEl.getHeight();
27788             this.baseScale = height / this.imageEl.OriginHeight;
27789         }
27790         
27791         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27792             
27793             height = this.thumbEl.getHeight();
27794             this.baseScale = height / this.imageEl.OriginHeight;
27795             
27796             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27797                 width = this.thumbEl.getWidth();
27798                 this.baseScale = width / this.imageEl.OriginWidth;
27799             }
27800             
27801         }
27802         
27803         return;
27804     },
27805     
27806     getScaleLevel : function()
27807     {
27808         return this.baseScale * Math.pow(1.1, this.scale);
27809     },
27810     
27811     onTouchStart : function(e)
27812     {
27813         if(!this.canvasLoaded){
27814             this.beforeSelectFile(e);
27815             return;
27816         }
27817         
27818         var touches = e.browserEvent.touches;
27819         
27820         if(!touches){
27821             return;
27822         }
27823         
27824         if(touches.length == 1){
27825             this.onMouseDown(e);
27826             return;
27827         }
27828         
27829         if(touches.length != 2){
27830             return;
27831         }
27832         
27833         var coords = [];
27834         
27835         for(var i = 0, finger; finger = touches[i]; i++){
27836             coords.push(finger.pageX, finger.pageY);
27837         }
27838         
27839         var x = Math.pow(coords[0] - coords[2], 2);
27840         var y = Math.pow(coords[1] - coords[3], 2);
27841         
27842         this.startDistance = Math.sqrt(x + y);
27843         
27844         this.startScale = this.scale;
27845         
27846         this.pinching = true;
27847         this.dragable = false;
27848         
27849     },
27850     
27851     onTouchMove : function(e)
27852     {
27853         if(!this.pinching && !this.dragable){
27854             return;
27855         }
27856         
27857         var touches = e.browserEvent.touches;
27858         
27859         if(!touches){
27860             return;
27861         }
27862         
27863         if(this.dragable){
27864             this.onMouseMove(e);
27865             return;
27866         }
27867         
27868         var coords = [];
27869         
27870         for(var i = 0, finger; finger = touches[i]; i++){
27871             coords.push(finger.pageX, finger.pageY);
27872         }
27873         
27874         var x = Math.pow(coords[0] - coords[2], 2);
27875         var y = Math.pow(coords[1] - coords[3], 2);
27876         
27877         this.endDistance = Math.sqrt(x + y);
27878         
27879         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27880         
27881         if(!this.zoomable()){
27882             this.scale = this.startScale;
27883             return;
27884         }
27885         
27886         this.draw();
27887         
27888     },
27889     
27890     onTouchEnd : function(e)
27891     {
27892         this.pinching = false;
27893         this.dragable = false;
27894         
27895     },
27896     
27897     process : function(file, crop)
27898     {
27899         if(this.loadMask){
27900             this.maskEl.mask(this.loadingText);
27901         }
27902         
27903         this.xhr = new XMLHttpRequest();
27904         
27905         file.xhr = this.xhr;
27906
27907         this.xhr.open(this.method, this.url, true);
27908         
27909         var headers = {
27910             "Accept": "application/json",
27911             "Cache-Control": "no-cache",
27912             "X-Requested-With": "XMLHttpRequest"
27913         };
27914         
27915         for (var headerName in headers) {
27916             var headerValue = headers[headerName];
27917             if (headerValue) {
27918                 this.xhr.setRequestHeader(headerName, headerValue);
27919             }
27920         }
27921         
27922         var _this = this;
27923         
27924         this.xhr.onload = function()
27925         {
27926             _this.xhrOnLoad(_this.xhr);
27927         }
27928         
27929         this.xhr.onerror = function()
27930         {
27931             _this.xhrOnError(_this.xhr);
27932         }
27933         
27934         var formData = new FormData();
27935
27936         formData.append('returnHTML', 'NO');
27937         
27938         if(crop){
27939             formData.append('crop', crop);
27940         }
27941         
27942         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27943             formData.append(this.paramName, file, file.name);
27944         }
27945         
27946         if(typeof(file.filename) != 'undefined'){
27947             formData.append('filename', file.filename);
27948         }
27949         
27950         if(typeof(file.mimetype) != 'undefined'){
27951             formData.append('mimetype', file.mimetype);
27952         }
27953         
27954         if(this.fireEvent('arrange', this, formData) != false){
27955             this.xhr.send(formData);
27956         };
27957     },
27958     
27959     xhrOnLoad : function(xhr)
27960     {
27961         if(this.loadMask){
27962             this.maskEl.unmask();
27963         }
27964         
27965         if (xhr.readyState !== 4) {
27966             this.fireEvent('exception', this, xhr);
27967             return;
27968         }
27969
27970         var response = Roo.decode(xhr.responseText);
27971         
27972         if(!response.success){
27973             this.fireEvent('exception', this, xhr);
27974             return;
27975         }
27976         
27977         var response = Roo.decode(xhr.responseText);
27978         
27979         this.fireEvent('upload', this, response);
27980         
27981     },
27982     
27983     xhrOnError : function()
27984     {
27985         if(this.loadMask){
27986             this.maskEl.unmask();
27987         }
27988         
27989         Roo.log('xhr on error');
27990         
27991         var response = Roo.decode(xhr.responseText);
27992           
27993         Roo.log(response);
27994         
27995     },
27996     
27997     prepare : function(file)
27998     {   
27999         if(this.loadMask){
28000             this.maskEl.mask(this.loadingText);
28001         }
28002         
28003         this.file = false;
28004         this.exif = {};
28005         
28006         if(typeof(file) === 'string'){
28007             this.loadCanvas(file);
28008             return;
28009         }
28010         
28011         if(!file || !this.urlAPI){
28012             return;
28013         }
28014         
28015         this.file = file;
28016         this.cropType = file.type;
28017         
28018         var _this = this;
28019         
28020         if(this.fireEvent('prepare', this, this.file) != false){
28021             
28022             var reader = new FileReader();
28023             
28024             reader.onload = function (e) {
28025                 if (e.target.error) {
28026                     Roo.log(e.target.error);
28027                     return;
28028                 }
28029                 
28030                 var buffer = e.target.result,
28031                     dataView = new DataView(buffer),
28032                     offset = 2,
28033                     maxOffset = dataView.byteLength - 4,
28034                     markerBytes,
28035                     markerLength;
28036                 
28037                 if (dataView.getUint16(0) === 0xffd8) {
28038                     while (offset < maxOffset) {
28039                         markerBytes = dataView.getUint16(offset);
28040                         
28041                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28042                             markerLength = dataView.getUint16(offset + 2) + 2;
28043                             if (offset + markerLength > dataView.byteLength) {
28044                                 Roo.log('Invalid meta data: Invalid segment size.');
28045                                 break;
28046                             }
28047                             
28048                             if(markerBytes == 0xffe1){
28049                                 _this.parseExifData(
28050                                     dataView,
28051                                     offset,
28052                                     markerLength
28053                                 );
28054                             }
28055                             
28056                             offset += markerLength;
28057                             
28058                             continue;
28059                         }
28060                         
28061                         break;
28062                     }
28063                     
28064                 }
28065                 
28066                 var url = _this.urlAPI.createObjectURL(_this.file);
28067                 
28068                 _this.loadCanvas(url);
28069                 
28070                 return;
28071             }
28072             
28073             reader.readAsArrayBuffer(this.file);
28074             
28075         }
28076         
28077     },
28078     
28079     parseExifData : function(dataView, offset, length)
28080     {
28081         var tiffOffset = offset + 10,
28082             littleEndian,
28083             dirOffset;
28084     
28085         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28086             // No Exif data, might be XMP data instead
28087             return;
28088         }
28089         
28090         // Check for the ASCII code for "Exif" (0x45786966):
28091         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28092             // No Exif data, might be XMP data instead
28093             return;
28094         }
28095         if (tiffOffset + 8 > dataView.byteLength) {
28096             Roo.log('Invalid Exif data: Invalid segment size.');
28097             return;
28098         }
28099         // Check for the two null bytes:
28100         if (dataView.getUint16(offset + 8) !== 0x0000) {
28101             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28102             return;
28103         }
28104         // Check the byte alignment:
28105         switch (dataView.getUint16(tiffOffset)) {
28106         case 0x4949:
28107             littleEndian = true;
28108             break;
28109         case 0x4D4D:
28110             littleEndian = false;
28111             break;
28112         default:
28113             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28114             return;
28115         }
28116         // Check for the TIFF tag marker (0x002A):
28117         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28118             Roo.log('Invalid Exif data: Missing TIFF marker.');
28119             return;
28120         }
28121         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28122         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28123         
28124         this.parseExifTags(
28125             dataView,
28126             tiffOffset,
28127             tiffOffset + dirOffset,
28128             littleEndian
28129         );
28130     },
28131     
28132     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28133     {
28134         var tagsNumber,
28135             dirEndOffset,
28136             i;
28137         if (dirOffset + 6 > dataView.byteLength) {
28138             Roo.log('Invalid Exif data: Invalid directory offset.');
28139             return;
28140         }
28141         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28142         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28143         if (dirEndOffset + 4 > dataView.byteLength) {
28144             Roo.log('Invalid Exif data: Invalid directory size.');
28145             return;
28146         }
28147         for (i = 0; i < tagsNumber; i += 1) {
28148             this.parseExifTag(
28149                 dataView,
28150                 tiffOffset,
28151                 dirOffset + 2 + 12 * i, // tag offset
28152                 littleEndian
28153             );
28154         }
28155         // Return the offset to the next directory:
28156         return dataView.getUint32(dirEndOffset, littleEndian);
28157     },
28158     
28159     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28160     {
28161         var tag = dataView.getUint16(offset, littleEndian);
28162         
28163         this.exif[tag] = this.getExifValue(
28164             dataView,
28165             tiffOffset,
28166             offset,
28167             dataView.getUint16(offset + 2, littleEndian), // tag type
28168             dataView.getUint32(offset + 4, littleEndian), // tag length
28169             littleEndian
28170         );
28171     },
28172     
28173     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28174     {
28175         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28176             tagSize,
28177             dataOffset,
28178             values,
28179             i,
28180             str,
28181             c;
28182     
28183         if (!tagType) {
28184             Roo.log('Invalid Exif data: Invalid tag type.');
28185             return;
28186         }
28187         
28188         tagSize = tagType.size * length;
28189         // Determine if the value is contained in the dataOffset bytes,
28190         // or if the value at the dataOffset is a pointer to the actual data:
28191         dataOffset = tagSize > 4 ?
28192                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28193         if (dataOffset + tagSize > dataView.byteLength) {
28194             Roo.log('Invalid Exif data: Invalid data offset.');
28195             return;
28196         }
28197         if (length === 1) {
28198             return tagType.getValue(dataView, dataOffset, littleEndian);
28199         }
28200         values = [];
28201         for (i = 0; i < length; i += 1) {
28202             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28203         }
28204         
28205         if (tagType.ascii) {
28206             str = '';
28207             // Concatenate the chars:
28208             for (i = 0; i < values.length; i += 1) {
28209                 c = values[i];
28210                 // Ignore the terminating NULL byte(s):
28211                 if (c === '\u0000') {
28212                     break;
28213                 }
28214                 str += c;
28215             }
28216             return str;
28217         }
28218         return values;
28219     }
28220     
28221 });
28222
28223 Roo.apply(Roo.bootstrap.UploadCropbox, {
28224     tags : {
28225         'Orientation': 0x0112
28226     },
28227     
28228     Orientation: {
28229             1: 0, //'top-left',
28230 //            2: 'top-right',
28231             3: 180, //'bottom-right',
28232 //            4: 'bottom-left',
28233 //            5: 'left-top',
28234             6: 90, //'right-top',
28235 //            7: 'right-bottom',
28236             8: 270 //'left-bottom'
28237     },
28238     
28239     exifTagTypes : {
28240         // byte, 8-bit unsigned int:
28241         1: {
28242             getValue: function (dataView, dataOffset) {
28243                 return dataView.getUint8(dataOffset);
28244             },
28245             size: 1
28246         },
28247         // ascii, 8-bit byte:
28248         2: {
28249             getValue: function (dataView, dataOffset) {
28250                 return String.fromCharCode(dataView.getUint8(dataOffset));
28251             },
28252             size: 1,
28253             ascii: true
28254         },
28255         // short, 16 bit int:
28256         3: {
28257             getValue: function (dataView, dataOffset, littleEndian) {
28258                 return dataView.getUint16(dataOffset, littleEndian);
28259             },
28260             size: 2
28261         },
28262         // long, 32 bit int:
28263         4: {
28264             getValue: function (dataView, dataOffset, littleEndian) {
28265                 return dataView.getUint32(dataOffset, littleEndian);
28266             },
28267             size: 4
28268         },
28269         // rational = two long values, first is numerator, second is denominator:
28270         5: {
28271             getValue: function (dataView, dataOffset, littleEndian) {
28272                 return dataView.getUint32(dataOffset, littleEndian) /
28273                     dataView.getUint32(dataOffset + 4, littleEndian);
28274             },
28275             size: 8
28276         },
28277         // slong, 32 bit signed int:
28278         9: {
28279             getValue: function (dataView, dataOffset, littleEndian) {
28280                 return dataView.getInt32(dataOffset, littleEndian);
28281             },
28282             size: 4
28283         },
28284         // srational, two slongs, first is numerator, second is denominator:
28285         10: {
28286             getValue: function (dataView, dataOffset, littleEndian) {
28287                 return dataView.getInt32(dataOffset, littleEndian) /
28288                     dataView.getInt32(dataOffset + 4, littleEndian);
28289             },
28290             size: 8
28291         }
28292     },
28293     
28294     footer : {
28295         STANDARD : [
28296             {
28297                 tag : 'div',
28298                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28299                 action : 'rotate-left',
28300                 cn : [
28301                     {
28302                         tag : 'button',
28303                         cls : 'btn btn-default',
28304                         html : '<i class="fa fa-undo"></i>'
28305                     }
28306                 ]
28307             },
28308             {
28309                 tag : 'div',
28310                 cls : 'btn-group roo-upload-cropbox-picture',
28311                 action : 'picture',
28312                 cn : [
28313                     {
28314                         tag : 'button',
28315                         cls : 'btn btn-default',
28316                         html : '<i class="fa fa-picture-o"></i>'
28317                     }
28318                 ]
28319             },
28320             {
28321                 tag : 'div',
28322                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28323                 action : 'rotate-right',
28324                 cn : [
28325                     {
28326                         tag : 'button',
28327                         cls : 'btn btn-default',
28328                         html : '<i class="fa fa-repeat"></i>'
28329                     }
28330                 ]
28331             }
28332         ],
28333         DOCUMENT : [
28334             {
28335                 tag : 'div',
28336                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28337                 action : 'rotate-left',
28338                 cn : [
28339                     {
28340                         tag : 'button',
28341                         cls : 'btn btn-default',
28342                         html : '<i class="fa fa-undo"></i>'
28343                     }
28344                 ]
28345             },
28346             {
28347                 tag : 'div',
28348                 cls : 'btn-group roo-upload-cropbox-download',
28349                 action : 'download',
28350                 cn : [
28351                     {
28352                         tag : 'button',
28353                         cls : 'btn btn-default',
28354                         html : '<i class="fa fa-download"></i>'
28355                     }
28356                 ]
28357             },
28358             {
28359                 tag : 'div',
28360                 cls : 'btn-group roo-upload-cropbox-crop',
28361                 action : 'crop',
28362                 cn : [
28363                     {
28364                         tag : 'button',
28365                         cls : 'btn btn-default',
28366                         html : '<i class="fa fa-crop"></i>'
28367                     }
28368                 ]
28369             },
28370             {
28371                 tag : 'div',
28372                 cls : 'btn-group roo-upload-cropbox-trash',
28373                 action : 'trash',
28374                 cn : [
28375                     {
28376                         tag : 'button',
28377                         cls : 'btn btn-default',
28378                         html : '<i class="fa fa-trash"></i>'
28379                     }
28380                 ]
28381             },
28382             {
28383                 tag : 'div',
28384                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28385                 action : 'rotate-right',
28386                 cn : [
28387                     {
28388                         tag : 'button',
28389                         cls : 'btn btn-default',
28390                         html : '<i class="fa fa-repeat"></i>'
28391                     }
28392                 ]
28393             }
28394         ],
28395         ROTATOR : [
28396             {
28397                 tag : 'div',
28398                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28399                 action : 'rotate-left',
28400                 cn : [
28401                     {
28402                         tag : 'button',
28403                         cls : 'btn btn-default',
28404                         html : '<i class="fa fa-undo"></i>'
28405                     }
28406                 ]
28407             },
28408             {
28409                 tag : 'div',
28410                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28411                 action : 'rotate-right',
28412                 cn : [
28413                     {
28414                         tag : 'button',
28415                         cls : 'btn btn-default',
28416                         html : '<i class="fa fa-repeat"></i>'
28417                     }
28418                 ]
28419             }
28420         ]
28421     }
28422 });
28423
28424 /*
28425 * Licence: LGPL
28426 */
28427
28428 /**
28429  * @class Roo.bootstrap.DocumentManager
28430  * @extends Roo.bootstrap.Component
28431  * Bootstrap DocumentManager class
28432  * @cfg {String} paramName default 'imageUpload'
28433  * @cfg {String} toolTipName default 'filename'
28434  * @cfg {String} method default POST
28435  * @cfg {String} url action url
28436  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28437  * @cfg {Boolean} multiple multiple upload default true
28438  * @cfg {Number} thumbSize default 300
28439  * @cfg {String} fieldLabel
28440  * @cfg {Number} labelWidth default 4
28441  * @cfg {String} labelAlign (left|top) default left
28442  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28443 * @cfg {Number} labellg set the width of label (1-12)
28444  * @cfg {Number} labelmd set the width of label (1-12)
28445  * @cfg {Number} labelsm set the width of label (1-12)
28446  * @cfg {Number} labelxs set the width of label (1-12)
28447  * 
28448  * @constructor
28449  * Create a new DocumentManager
28450  * @param {Object} config The config object
28451  */
28452
28453 Roo.bootstrap.DocumentManager = function(config){
28454     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28455     
28456     this.files = [];
28457     this.delegates = [];
28458     
28459     this.addEvents({
28460         /**
28461          * @event initial
28462          * Fire when initial the DocumentManager
28463          * @param {Roo.bootstrap.DocumentManager} this
28464          */
28465         "initial" : true,
28466         /**
28467          * @event inspect
28468          * inspect selected file
28469          * @param {Roo.bootstrap.DocumentManager} this
28470          * @param {File} file
28471          */
28472         "inspect" : true,
28473         /**
28474          * @event exception
28475          * Fire when xhr load exception
28476          * @param {Roo.bootstrap.DocumentManager} this
28477          * @param {XMLHttpRequest} xhr
28478          */
28479         "exception" : true,
28480         /**
28481          * @event afterupload
28482          * Fire when xhr load exception
28483          * @param {Roo.bootstrap.DocumentManager} this
28484          * @param {XMLHttpRequest} xhr
28485          */
28486         "afterupload" : true,
28487         /**
28488          * @event prepare
28489          * prepare the form data
28490          * @param {Roo.bootstrap.DocumentManager} this
28491          * @param {Object} formData
28492          */
28493         "prepare" : true,
28494         /**
28495          * @event remove
28496          * Fire when remove the file
28497          * @param {Roo.bootstrap.DocumentManager} this
28498          * @param {Object} file
28499          */
28500         "remove" : true,
28501         /**
28502          * @event refresh
28503          * Fire after refresh the file
28504          * @param {Roo.bootstrap.DocumentManager} this
28505          */
28506         "refresh" : true,
28507         /**
28508          * @event click
28509          * Fire after click the image
28510          * @param {Roo.bootstrap.DocumentManager} this
28511          * @param {Object} file
28512          */
28513         "click" : true,
28514         /**
28515          * @event edit
28516          * Fire when upload a image and editable set to true
28517          * @param {Roo.bootstrap.DocumentManager} this
28518          * @param {Object} file
28519          */
28520         "edit" : true,
28521         /**
28522          * @event beforeselectfile
28523          * Fire before select file
28524          * @param {Roo.bootstrap.DocumentManager} this
28525          */
28526         "beforeselectfile" : true,
28527         /**
28528          * @event process
28529          * Fire before process file
28530          * @param {Roo.bootstrap.DocumentManager} this
28531          * @param {Object} file
28532          */
28533         "process" : true,
28534         /**
28535          * @event previewrendered
28536          * Fire when preview rendered
28537          * @param {Roo.bootstrap.DocumentManager} this
28538          * @param {Object} file
28539          */
28540         "previewrendered" : true
28541         
28542     });
28543 };
28544
28545 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28546     
28547     boxes : 0,
28548     inputName : '',
28549     thumbSize : 300,
28550     multiple : true,
28551     files : false,
28552     method : 'POST',
28553     url : '',
28554     paramName : 'imageUpload',
28555     toolTipName : 'filename',
28556     fieldLabel : '',
28557     labelWidth : 4,
28558     labelAlign : 'left',
28559     editable : true,
28560     delegates : false,
28561     xhr : false, 
28562     
28563     labellg : 0,
28564     labelmd : 0,
28565     labelsm : 0,
28566     labelxs : 0,
28567     
28568     getAutoCreate : function()
28569     {   
28570         var managerWidget = {
28571             tag : 'div',
28572             cls : 'roo-document-manager',
28573             cn : [
28574                 {
28575                     tag : 'input',
28576                     cls : 'roo-document-manager-selector',
28577                     type : 'file'
28578                 },
28579                 {
28580                     tag : 'div',
28581                     cls : 'roo-document-manager-uploader',
28582                     cn : [
28583                         {
28584                             tag : 'div',
28585                             cls : 'roo-document-manager-upload-btn',
28586                             html : '<i class="fa fa-plus"></i>'
28587                         }
28588                     ]
28589                     
28590                 }
28591             ]
28592         };
28593         
28594         var content = [
28595             {
28596                 tag : 'div',
28597                 cls : 'column col-md-12',
28598                 cn : managerWidget
28599             }
28600         ];
28601         
28602         if(this.fieldLabel.length){
28603             
28604             content = [
28605                 {
28606                     tag : 'div',
28607                     cls : 'column col-md-12',
28608                     html : this.fieldLabel
28609                 },
28610                 {
28611                     tag : 'div',
28612                     cls : 'column col-md-12',
28613                     cn : managerWidget
28614                 }
28615             ];
28616
28617             if(this.labelAlign == 'left'){
28618                 content = [
28619                     {
28620                         tag : 'div',
28621                         cls : 'column',
28622                         html : this.fieldLabel
28623                     },
28624                     {
28625                         tag : 'div',
28626                         cls : 'column',
28627                         cn : managerWidget
28628                     }
28629                 ];
28630                 
28631                 if(this.labelWidth > 12){
28632                     content[0].style = "width: " + this.labelWidth + 'px';
28633                 }
28634
28635                 if(this.labelWidth < 13 && this.labelmd == 0){
28636                     this.labelmd = this.labelWidth;
28637                 }
28638
28639                 if(this.labellg > 0){
28640                     content[0].cls += ' col-lg-' + this.labellg;
28641                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28642                 }
28643
28644                 if(this.labelmd > 0){
28645                     content[0].cls += ' col-md-' + this.labelmd;
28646                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28647                 }
28648
28649                 if(this.labelsm > 0){
28650                     content[0].cls += ' col-sm-' + this.labelsm;
28651                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28652                 }
28653
28654                 if(this.labelxs > 0){
28655                     content[0].cls += ' col-xs-' + this.labelxs;
28656                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28657                 }
28658                 
28659             }
28660         }
28661         
28662         var cfg = {
28663             tag : 'div',
28664             cls : 'row clearfix',
28665             cn : content
28666         };
28667         
28668         return cfg;
28669         
28670     },
28671     
28672     initEvents : function()
28673     {
28674         this.managerEl = this.el.select('.roo-document-manager', true).first();
28675         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28676         
28677         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28678         this.selectorEl.hide();
28679         
28680         if(this.multiple){
28681             this.selectorEl.attr('multiple', 'multiple');
28682         }
28683         
28684         this.selectorEl.on('change', this.onFileSelected, this);
28685         
28686         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28687         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28688         
28689         this.uploader.on('click', this.onUploaderClick, this);
28690         
28691         this.renderProgressDialog();
28692         
28693         var _this = this;
28694         
28695         window.addEventListener("resize", function() { _this.refresh(); } );
28696         
28697         this.fireEvent('initial', this);
28698     },
28699     
28700     renderProgressDialog : function()
28701     {
28702         var _this = this;
28703         
28704         this.progressDialog = new Roo.bootstrap.Modal({
28705             cls : 'roo-document-manager-progress-dialog',
28706             allow_close : false,
28707             title : '',
28708             buttons : [
28709                 {
28710                     name  :'cancel',
28711                     weight : 'danger',
28712                     html : 'Cancel'
28713                 }
28714             ], 
28715             listeners : { 
28716                 btnclick : function() {
28717                     _this.uploadCancel();
28718                     this.hide();
28719                 }
28720             }
28721         });
28722          
28723         this.progressDialog.render(Roo.get(document.body));
28724          
28725         this.progress = new Roo.bootstrap.Progress({
28726             cls : 'roo-document-manager-progress',
28727             active : true,
28728             striped : true
28729         });
28730         
28731         this.progress.render(this.progressDialog.getChildContainer());
28732         
28733         this.progressBar = new Roo.bootstrap.ProgressBar({
28734             cls : 'roo-document-manager-progress-bar',
28735             aria_valuenow : 0,
28736             aria_valuemin : 0,
28737             aria_valuemax : 12,
28738             panel : 'success'
28739         });
28740         
28741         this.progressBar.render(this.progress.getChildContainer());
28742     },
28743     
28744     onUploaderClick : function(e)
28745     {
28746         e.preventDefault();
28747      
28748         if(this.fireEvent('beforeselectfile', this) != false){
28749             this.selectorEl.dom.click();
28750         }
28751         
28752     },
28753     
28754     onFileSelected : function(e)
28755     {
28756         e.preventDefault();
28757         
28758         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28759             return;
28760         }
28761         
28762         Roo.each(this.selectorEl.dom.files, function(file){
28763             if(this.fireEvent('inspect', this, file) != false){
28764                 this.files.push(file);
28765             }
28766         }, this);
28767         
28768         this.queue();
28769         
28770     },
28771     
28772     queue : function()
28773     {
28774         this.selectorEl.dom.value = '';
28775         
28776         if(!this.files || !this.files.length){
28777             return;
28778         }
28779         
28780         if(this.boxes > 0 && this.files.length > this.boxes){
28781             this.files = this.files.slice(0, this.boxes);
28782         }
28783         
28784         this.uploader.show();
28785         
28786         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28787             this.uploader.hide();
28788         }
28789         
28790         var _this = this;
28791         
28792         var files = [];
28793         
28794         var docs = [];
28795         
28796         Roo.each(this.files, function(file){
28797             
28798             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28799                 var f = this.renderPreview(file);
28800                 files.push(f);
28801                 return;
28802             }
28803             
28804             if(file.type.indexOf('image') != -1){
28805                 this.delegates.push(
28806                     (function(){
28807                         _this.process(file);
28808                     }).createDelegate(this)
28809                 );
28810         
28811                 return;
28812             }
28813             
28814             docs.push(
28815                 (function(){
28816                     _this.process(file);
28817                 }).createDelegate(this)
28818             );
28819             
28820         }, this);
28821         
28822         this.files = files;
28823         
28824         this.delegates = this.delegates.concat(docs);
28825         
28826         if(!this.delegates.length){
28827             this.refresh();
28828             return;
28829         }
28830         
28831         this.progressBar.aria_valuemax = this.delegates.length;
28832         
28833         this.arrange();
28834         
28835         return;
28836     },
28837     
28838     arrange : function()
28839     {
28840         if(!this.delegates.length){
28841             this.progressDialog.hide();
28842             this.refresh();
28843             return;
28844         }
28845         
28846         var delegate = this.delegates.shift();
28847         
28848         this.progressDialog.show();
28849         
28850         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28851         
28852         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28853         
28854         delegate();
28855     },
28856     
28857     refresh : function()
28858     {
28859         this.uploader.show();
28860         
28861         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28862             this.uploader.hide();
28863         }
28864         
28865         Roo.isTouch ? this.closable(false) : this.closable(true);
28866         
28867         this.fireEvent('refresh', this);
28868     },
28869     
28870     onRemove : function(e, el, o)
28871     {
28872         e.preventDefault();
28873         
28874         this.fireEvent('remove', this, o);
28875         
28876     },
28877     
28878     remove : function(o)
28879     {
28880         var files = [];
28881         
28882         Roo.each(this.files, function(file){
28883             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28884                 files.push(file);
28885                 return;
28886             }
28887
28888             o.target.remove();
28889
28890         }, this);
28891         
28892         this.files = files;
28893         
28894         this.refresh();
28895     },
28896     
28897     clear : function()
28898     {
28899         Roo.each(this.files, function(file){
28900             if(!file.target){
28901                 return;
28902             }
28903             
28904             file.target.remove();
28905
28906         }, this);
28907         
28908         this.files = [];
28909         
28910         this.refresh();
28911     },
28912     
28913     onClick : function(e, el, o)
28914     {
28915         e.preventDefault();
28916         
28917         this.fireEvent('click', this, o);
28918         
28919     },
28920     
28921     closable : function(closable)
28922     {
28923         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28924             
28925             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28926             
28927             if(closable){
28928                 el.show();
28929                 return;
28930             }
28931             
28932             el.hide();
28933             
28934         }, this);
28935     },
28936     
28937     xhrOnLoad : function(xhr)
28938     {
28939         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28940             el.remove();
28941         }, this);
28942         
28943         if (xhr.readyState !== 4) {
28944             this.arrange();
28945             this.fireEvent('exception', this, xhr);
28946             return;
28947         }
28948
28949         var response = Roo.decode(xhr.responseText);
28950         
28951         if(!response.success){
28952             this.arrange();
28953             this.fireEvent('exception', this, xhr);
28954             return;
28955         }
28956         
28957         var file = this.renderPreview(response.data);
28958         
28959         this.files.push(file);
28960         
28961         this.arrange();
28962         
28963         this.fireEvent('afterupload', this, xhr);
28964         
28965     },
28966     
28967     xhrOnError : function(xhr)
28968     {
28969         Roo.log('xhr on error');
28970         
28971         var response = Roo.decode(xhr.responseText);
28972           
28973         Roo.log(response);
28974         
28975         this.arrange();
28976     },
28977     
28978     process : function(file)
28979     {
28980         if(this.fireEvent('process', this, file) !== false){
28981             if(this.editable && file.type.indexOf('image') != -1){
28982                 this.fireEvent('edit', this, file);
28983                 return;
28984             }
28985
28986             this.uploadStart(file, false);
28987
28988             return;
28989         }
28990         
28991     },
28992     
28993     uploadStart : function(file, crop)
28994     {
28995         this.xhr = new XMLHttpRequest();
28996         
28997         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28998             this.arrange();
28999             return;
29000         }
29001         
29002         file.xhr = this.xhr;
29003             
29004         this.managerEl.createChild({
29005             tag : 'div',
29006             cls : 'roo-document-manager-loading',
29007             cn : [
29008                 {
29009                     tag : 'div',
29010                     tooltip : file.name,
29011                     cls : 'roo-document-manager-thumb',
29012                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29013                 }
29014             ]
29015
29016         });
29017
29018         this.xhr.open(this.method, this.url, true);
29019         
29020         var headers = {
29021             "Accept": "application/json",
29022             "Cache-Control": "no-cache",
29023             "X-Requested-With": "XMLHttpRequest"
29024         };
29025         
29026         for (var headerName in headers) {
29027             var headerValue = headers[headerName];
29028             if (headerValue) {
29029                 this.xhr.setRequestHeader(headerName, headerValue);
29030             }
29031         }
29032         
29033         var _this = this;
29034         
29035         this.xhr.onload = function()
29036         {
29037             _this.xhrOnLoad(_this.xhr);
29038         }
29039         
29040         this.xhr.onerror = function()
29041         {
29042             _this.xhrOnError(_this.xhr);
29043         }
29044         
29045         var formData = new FormData();
29046
29047         formData.append('returnHTML', 'NO');
29048         
29049         if(crop){
29050             formData.append('crop', crop);
29051         }
29052         
29053         formData.append(this.paramName, file, file.name);
29054         
29055         var options = {
29056             file : file, 
29057             manually : false
29058         };
29059         
29060         if(this.fireEvent('prepare', this, formData, options) != false){
29061             
29062             if(options.manually){
29063                 return;
29064             }
29065             
29066             this.xhr.send(formData);
29067             return;
29068         };
29069         
29070         this.uploadCancel();
29071     },
29072     
29073     uploadCancel : function()
29074     {
29075         if (this.xhr) {
29076             this.xhr.abort();
29077         }
29078         
29079         this.delegates = [];
29080         
29081         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29082             el.remove();
29083         }, this);
29084         
29085         this.arrange();
29086     },
29087     
29088     renderPreview : function(file)
29089     {
29090         if(typeof(file.target) != 'undefined' && file.target){
29091             return file;
29092         }
29093         
29094         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29095         
29096         var previewEl = this.managerEl.createChild({
29097             tag : 'div',
29098             cls : 'roo-document-manager-preview',
29099             cn : [
29100                 {
29101                     tag : 'div',
29102                     tooltip : file[this.toolTipName],
29103                     cls : 'roo-document-manager-thumb',
29104                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29105                 },
29106                 {
29107                     tag : 'button',
29108                     cls : 'close',
29109                     html : '<i class="fa fa-times-circle"></i>'
29110                 }
29111             ]
29112         });
29113
29114         var close = previewEl.select('button.close', true).first();
29115
29116         close.on('click', this.onRemove, this, file);
29117
29118         file.target = previewEl;
29119
29120         var image = previewEl.select('img', true).first();
29121         
29122         var _this = this;
29123         
29124         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29125         
29126         image.on('click', this.onClick, this, file);
29127         
29128         this.fireEvent('previewrendered', this, file);
29129         
29130         return file;
29131         
29132     },
29133     
29134     onPreviewLoad : function(file, image)
29135     {
29136         if(typeof(file.target) == 'undefined' || !file.target){
29137             return;
29138         }
29139         
29140         var width = image.dom.naturalWidth || image.dom.width;
29141         var height = image.dom.naturalHeight || image.dom.height;
29142         
29143         if(width > height){
29144             file.target.addClass('wide');
29145             return;
29146         }
29147         
29148         file.target.addClass('tall');
29149         return;
29150         
29151     },
29152     
29153     uploadFromSource : function(file, crop)
29154     {
29155         this.xhr = new XMLHttpRequest();
29156         
29157         this.managerEl.createChild({
29158             tag : 'div',
29159             cls : 'roo-document-manager-loading',
29160             cn : [
29161                 {
29162                     tag : 'div',
29163                     tooltip : file.name,
29164                     cls : 'roo-document-manager-thumb',
29165                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29166                 }
29167             ]
29168
29169         });
29170
29171         this.xhr.open(this.method, this.url, true);
29172         
29173         var headers = {
29174             "Accept": "application/json",
29175             "Cache-Control": "no-cache",
29176             "X-Requested-With": "XMLHttpRequest"
29177         };
29178         
29179         for (var headerName in headers) {
29180             var headerValue = headers[headerName];
29181             if (headerValue) {
29182                 this.xhr.setRequestHeader(headerName, headerValue);
29183             }
29184         }
29185         
29186         var _this = this;
29187         
29188         this.xhr.onload = function()
29189         {
29190             _this.xhrOnLoad(_this.xhr);
29191         }
29192         
29193         this.xhr.onerror = function()
29194         {
29195             _this.xhrOnError(_this.xhr);
29196         }
29197         
29198         var formData = new FormData();
29199
29200         formData.append('returnHTML', 'NO');
29201         
29202         formData.append('crop', crop);
29203         
29204         if(typeof(file.filename) != 'undefined'){
29205             formData.append('filename', file.filename);
29206         }
29207         
29208         if(typeof(file.mimetype) != 'undefined'){
29209             formData.append('mimetype', file.mimetype);
29210         }
29211         
29212         Roo.log(formData);
29213         
29214         if(this.fireEvent('prepare', this, formData) != false){
29215             this.xhr.send(formData);
29216         };
29217     }
29218 });
29219
29220 /*
29221 * Licence: LGPL
29222 */
29223
29224 /**
29225  * @class Roo.bootstrap.DocumentViewer
29226  * @extends Roo.bootstrap.Component
29227  * Bootstrap DocumentViewer class
29228  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29229  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29230  * 
29231  * @constructor
29232  * Create a new DocumentViewer
29233  * @param {Object} config The config object
29234  */
29235
29236 Roo.bootstrap.DocumentViewer = function(config){
29237     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29238     
29239     this.addEvents({
29240         /**
29241          * @event initial
29242          * Fire after initEvent
29243          * @param {Roo.bootstrap.DocumentViewer} this
29244          */
29245         "initial" : true,
29246         /**
29247          * @event click
29248          * Fire after click
29249          * @param {Roo.bootstrap.DocumentViewer} this
29250          */
29251         "click" : true,
29252         /**
29253          * @event download
29254          * Fire after download button
29255          * @param {Roo.bootstrap.DocumentViewer} this
29256          */
29257         "download" : true,
29258         /**
29259          * @event trash
29260          * Fire after trash button
29261          * @param {Roo.bootstrap.DocumentViewer} this
29262          */
29263         "trash" : true
29264         
29265     });
29266 };
29267
29268 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29269     
29270     showDownload : true,
29271     
29272     showTrash : true,
29273     
29274     getAutoCreate : function()
29275     {
29276         var cfg = {
29277             tag : 'div',
29278             cls : 'roo-document-viewer',
29279             cn : [
29280                 {
29281                     tag : 'div',
29282                     cls : 'roo-document-viewer-body',
29283                     cn : [
29284                         {
29285                             tag : 'div',
29286                             cls : 'roo-document-viewer-thumb',
29287                             cn : [
29288                                 {
29289                                     tag : 'img',
29290                                     cls : 'roo-document-viewer-image'
29291                                 }
29292                             ]
29293                         }
29294                     ]
29295                 },
29296                 {
29297                     tag : 'div',
29298                     cls : 'roo-document-viewer-footer',
29299                     cn : {
29300                         tag : 'div',
29301                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29302                         cn : [
29303                             {
29304                                 tag : 'div',
29305                                 cls : 'btn-group roo-document-viewer-download',
29306                                 cn : [
29307                                     {
29308                                         tag : 'button',
29309                                         cls : 'btn btn-default',
29310                                         html : '<i class="fa fa-download"></i>'
29311                                     }
29312                                 ]
29313                             },
29314                             {
29315                                 tag : 'div',
29316                                 cls : 'btn-group roo-document-viewer-trash',
29317                                 cn : [
29318                                     {
29319                                         tag : 'button',
29320                                         cls : 'btn btn-default',
29321                                         html : '<i class="fa fa-trash"></i>'
29322                                     }
29323                                 ]
29324                             }
29325                         ]
29326                     }
29327                 }
29328             ]
29329         };
29330         
29331         return cfg;
29332     },
29333     
29334     initEvents : function()
29335     {
29336         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29337         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29338         
29339         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29340         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29341         
29342         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29343         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29344         
29345         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29346         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29347         
29348         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29349         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29350         
29351         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29352         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29353         
29354         this.bodyEl.on('click', this.onClick, this);
29355         this.downloadBtn.on('click', this.onDownload, this);
29356         this.trashBtn.on('click', this.onTrash, this);
29357         
29358         this.downloadBtn.hide();
29359         this.trashBtn.hide();
29360         
29361         if(this.showDownload){
29362             this.downloadBtn.show();
29363         }
29364         
29365         if(this.showTrash){
29366             this.trashBtn.show();
29367         }
29368         
29369         if(!this.showDownload && !this.showTrash) {
29370             this.footerEl.hide();
29371         }
29372         
29373     },
29374     
29375     initial : function()
29376     {
29377         this.fireEvent('initial', this);
29378         
29379     },
29380     
29381     onClick : function(e)
29382     {
29383         e.preventDefault();
29384         
29385         this.fireEvent('click', this);
29386     },
29387     
29388     onDownload : function(e)
29389     {
29390         e.preventDefault();
29391         
29392         this.fireEvent('download', this);
29393     },
29394     
29395     onTrash : function(e)
29396     {
29397         e.preventDefault();
29398         
29399         this.fireEvent('trash', this);
29400     }
29401     
29402 });
29403 /*
29404  * - LGPL
29405  *
29406  * nav progress bar
29407  * 
29408  */
29409
29410 /**
29411  * @class Roo.bootstrap.NavProgressBar
29412  * @extends Roo.bootstrap.Component
29413  * Bootstrap NavProgressBar class
29414  * 
29415  * @constructor
29416  * Create a new nav progress bar
29417  * @param {Object} config The config object
29418  */
29419
29420 Roo.bootstrap.NavProgressBar = function(config){
29421     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29422
29423     this.bullets = this.bullets || [];
29424    
29425 //    Roo.bootstrap.NavProgressBar.register(this);
29426      this.addEvents({
29427         /**
29428              * @event changed
29429              * Fires when the active item changes
29430              * @param {Roo.bootstrap.NavProgressBar} this
29431              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29432              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29433          */
29434         'changed': true
29435      });
29436     
29437 };
29438
29439 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29440     
29441     bullets : [],
29442     barItems : [],
29443     
29444     getAutoCreate : function()
29445     {
29446         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29447         
29448         cfg = {
29449             tag : 'div',
29450             cls : 'roo-navigation-bar-group',
29451             cn : [
29452                 {
29453                     tag : 'div',
29454                     cls : 'roo-navigation-top-bar'
29455                 },
29456                 {
29457                     tag : 'div',
29458                     cls : 'roo-navigation-bullets-bar',
29459                     cn : [
29460                         {
29461                             tag : 'ul',
29462                             cls : 'roo-navigation-bar'
29463                         }
29464                     ]
29465                 },
29466                 
29467                 {
29468                     tag : 'div',
29469                     cls : 'roo-navigation-bottom-bar'
29470                 }
29471             ]
29472             
29473         };
29474         
29475         return cfg;
29476         
29477     },
29478     
29479     initEvents: function() 
29480     {
29481         
29482     },
29483     
29484     onRender : function(ct, position) 
29485     {
29486         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29487         
29488         if(this.bullets.length){
29489             Roo.each(this.bullets, function(b){
29490                this.addItem(b);
29491             }, this);
29492         }
29493         
29494         this.format();
29495         
29496     },
29497     
29498     addItem : function(cfg)
29499     {
29500         var item = new Roo.bootstrap.NavProgressItem(cfg);
29501         
29502         item.parentId = this.id;
29503         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29504         
29505         if(cfg.html){
29506             var top = new Roo.bootstrap.Element({
29507                 tag : 'div',
29508                 cls : 'roo-navigation-bar-text'
29509             });
29510             
29511             var bottom = new Roo.bootstrap.Element({
29512                 tag : 'div',
29513                 cls : 'roo-navigation-bar-text'
29514             });
29515             
29516             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29517             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29518             
29519             var topText = new Roo.bootstrap.Element({
29520                 tag : 'span',
29521                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29522             });
29523             
29524             var bottomText = new Roo.bootstrap.Element({
29525                 tag : 'span',
29526                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29527             });
29528             
29529             topText.onRender(top.el, null);
29530             bottomText.onRender(bottom.el, null);
29531             
29532             item.topEl = top;
29533             item.bottomEl = bottom;
29534         }
29535         
29536         this.barItems.push(item);
29537         
29538         return item;
29539     },
29540     
29541     getActive : function()
29542     {
29543         var active = false;
29544         
29545         Roo.each(this.barItems, function(v){
29546             
29547             if (!v.isActive()) {
29548                 return;
29549             }
29550             
29551             active = v;
29552             return false;
29553             
29554         });
29555         
29556         return active;
29557     },
29558     
29559     setActiveItem : function(item)
29560     {
29561         var prev = false;
29562         
29563         Roo.each(this.barItems, function(v){
29564             if (v.rid == item.rid) {
29565                 return ;
29566             }
29567             
29568             if (v.isActive()) {
29569                 v.setActive(false);
29570                 prev = v;
29571             }
29572         });
29573
29574         item.setActive(true);
29575         
29576         this.fireEvent('changed', this, item, prev);
29577     },
29578     
29579     getBarItem: function(rid)
29580     {
29581         var ret = false;
29582         
29583         Roo.each(this.barItems, function(e) {
29584             if (e.rid != rid) {
29585                 return;
29586             }
29587             
29588             ret =  e;
29589             return false;
29590         });
29591         
29592         return ret;
29593     },
29594     
29595     indexOfItem : function(item)
29596     {
29597         var index = false;
29598         
29599         Roo.each(this.barItems, function(v, i){
29600             
29601             if (v.rid != item.rid) {
29602                 return;
29603             }
29604             
29605             index = i;
29606             return false
29607         });
29608         
29609         return index;
29610     },
29611     
29612     setActiveNext : function()
29613     {
29614         var i = this.indexOfItem(this.getActive());
29615         
29616         if (i > this.barItems.length) {
29617             return;
29618         }
29619         
29620         this.setActiveItem(this.barItems[i+1]);
29621     },
29622     
29623     setActivePrev : function()
29624     {
29625         var i = this.indexOfItem(this.getActive());
29626         
29627         if (i  < 1) {
29628             return;
29629         }
29630         
29631         this.setActiveItem(this.barItems[i-1]);
29632     },
29633     
29634     format : function()
29635     {
29636         if(!this.barItems.length){
29637             return;
29638         }
29639      
29640         var width = 100 / this.barItems.length;
29641         
29642         Roo.each(this.barItems, function(i){
29643             i.el.setStyle('width', width + '%');
29644             i.topEl.el.setStyle('width', width + '%');
29645             i.bottomEl.el.setStyle('width', width + '%');
29646         }, this);
29647         
29648     }
29649     
29650 });
29651 /*
29652  * - LGPL
29653  *
29654  * Nav Progress Item
29655  * 
29656  */
29657
29658 /**
29659  * @class Roo.bootstrap.NavProgressItem
29660  * @extends Roo.bootstrap.Component
29661  * Bootstrap NavProgressItem class
29662  * @cfg {String} rid the reference id
29663  * @cfg {Boolean} active (true|false) Is item active default false
29664  * @cfg {Boolean} disabled (true|false) Is item active default false
29665  * @cfg {String} html
29666  * @cfg {String} position (top|bottom) text position default bottom
29667  * @cfg {String} icon show icon instead of number
29668  * 
29669  * @constructor
29670  * Create a new NavProgressItem
29671  * @param {Object} config The config object
29672  */
29673 Roo.bootstrap.NavProgressItem = function(config){
29674     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29675     this.addEvents({
29676         // raw events
29677         /**
29678          * @event click
29679          * The raw click event for the entire grid.
29680          * @param {Roo.bootstrap.NavProgressItem} this
29681          * @param {Roo.EventObject} e
29682          */
29683         "click" : true
29684     });
29685    
29686 };
29687
29688 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29689     
29690     rid : '',
29691     active : false,
29692     disabled : false,
29693     html : '',
29694     position : 'bottom',
29695     icon : false,
29696     
29697     getAutoCreate : function()
29698     {
29699         var iconCls = 'roo-navigation-bar-item-icon';
29700         
29701         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29702         
29703         var cfg = {
29704             tag: 'li',
29705             cls: 'roo-navigation-bar-item',
29706             cn : [
29707                 {
29708                     tag : 'i',
29709                     cls : iconCls
29710                 }
29711             ]
29712         };
29713         
29714         if(this.active){
29715             cfg.cls += ' active';
29716         }
29717         if(this.disabled){
29718             cfg.cls += ' disabled';
29719         }
29720         
29721         return cfg;
29722     },
29723     
29724     disable : function()
29725     {
29726         this.setDisabled(true);
29727     },
29728     
29729     enable : function()
29730     {
29731         this.setDisabled(false);
29732     },
29733     
29734     initEvents: function() 
29735     {
29736         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29737         
29738         this.iconEl.on('click', this.onClick, this);
29739     },
29740     
29741     onClick : function(e)
29742     {
29743         e.preventDefault();
29744         
29745         if(this.disabled){
29746             return;
29747         }
29748         
29749         if(this.fireEvent('click', this, e) === false){
29750             return;
29751         };
29752         
29753         this.parent().setActiveItem(this);
29754     },
29755     
29756     isActive: function () 
29757     {
29758         return this.active;
29759     },
29760     
29761     setActive : function(state)
29762     {
29763         if(this.active == state){
29764             return;
29765         }
29766         
29767         this.active = state;
29768         
29769         if (state) {
29770             this.el.addClass('active');
29771             return;
29772         }
29773         
29774         this.el.removeClass('active');
29775         
29776         return;
29777     },
29778     
29779     setDisabled : function(state)
29780     {
29781         if(this.disabled == state){
29782             return;
29783         }
29784         
29785         this.disabled = state;
29786         
29787         if (state) {
29788             this.el.addClass('disabled');
29789             return;
29790         }
29791         
29792         this.el.removeClass('disabled');
29793     },
29794     
29795     tooltipEl : function()
29796     {
29797         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29798     }
29799 });
29800  
29801
29802  /*
29803  * - LGPL
29804  *
29805  * FieldLabel
29806  * 
29807  */
29808
29809 /**
29810  * @class Roo.bootstrap.FieldLabel
29811  * @extends Roo.bootstrap.Component
29812  * Bootstrap FieldLabel class
29813  * @cfg {String} html contents of the element
29814  * @cfg {String} tag tag of the element default label
29815  * @cfg {String} cls class of the element
29816  * @cfg {String} target label target 
29817  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29818  * @cfg {String} invalidClass default "text-warning"
29819  * @cfg {String} validClass default "text-success"
29820  * @cfg {String} iconTooltip default "This field is required"
29821  * @cfg {String} indicatorpos (left|right) default left
29822  * 
29823  * @constructor
29824  * Create a new FieldLabel
29825  * @param {Object} config The config object
29826  */
29827
29828 Roo.bootstrap.FieldLabel = function(config){
29829     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29830     
29831     this.addEvents({
29832             /**
29833              * @event invalid
29834              * Fires after the field has been marked as invalid.
29835              * @param {Roo.form.FieldLabel} this
29836              * @param {String} msg The validation message
29837              */
29838             invalid : true,
29839             /**
29840              * @event valid
29841              * Fires after the field has been validated with no errors.
29842              * @param {Roo.form.FieldLabel} this
29843              */
29844             valid : true
29845         });
29846 };
29847
29848 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29849     
29850     tag: 'label',
29851     cls: '',
29852     html: '',
29853     target: '',
29854     allowBlank : true,
29855     invalidClass : 'has-warning',
29856     validClass : 'has-success',
29857     iconTooltip : 'This field is required',
29858     indicatorpos : 'left',
29859     
29860     getAutoCreate : function(){
29861         
29862         var cfg = {
29863             tag : this.tag,
29864             cls : 'roo-bootstrap-field-label ' + this.cls,
29865             for : this.target,
29866             cn : [
29867                 {
29868                     tag : 'i',
29869                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29870                     tooltip : this.iconTooltip
29871                 },
29872                 {
29873                     tag : 'span',
29874                     html : this.html
29875                 }
29876             ] 
29877         };
29878         
29879         if(this.indicatorpos == 'right'){
29880             var cfg = {
29881                 tag : this.tag,
29882                 cls : 'roo-bootstrap-field-label ' + this.cls,
29883                 for : this.target,
29884                 cn : [
29885                     {
29886                         tag : 'span',
29887                         html : this.html
29888                     },
29889                     {
29890                         tag : 'i',
29891                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29892                         tooltip : this.iconTooltip
29893                     }
29894                 ] 
29895             };
29896         }
29897         
29898         return cfg;
29899     },
29900     
29901     initEvents: function() 
29902     {
29903         Roo.bootstrap.Element.superclass.initEvents.call(this);
29904         
29905         this.indicator = this.indicatorEl();
29906         
29907         if(this.indicator){
29908             this.indicator.removeClass('visible');
29909             this.indicator.addClass('invisible');
29910         }
29911         
29912         Roo.bootstrap.FieldLabel.register(this);
29913     },
29914     
29915     indicatorEl : function()
29916     {
29917         var indicator = this.el.select('i.roo-required-indicator',true).first();
29918         
29919         if(!indicator){
29920             return false;
29921         }
29922         
29923         return indicator;
29924         
29925     },
29926     
29927     /**
29928      * Mark this field as valid
29929      */
29930     markValid : function()
29931     {
29932         if(this.indicator){
29933             this.indicator.removeClass('visible');
29934             this.indicator.addClass('invisible');
29935         }
29936         
29937         this.el.removeClass(this.invalidClass);
29938         
29939         this.el.addClass(this.validClass);
29940         
29941         this.fireEvent('valid', this);
29942     },
29943     
29944     /**
29945      * Mark this field as invalid
29946      * @param {String} msg The validation message
29947      */
29948     markInvalid : function(msg)
29949     {
29950         if(this.indicator){
29951             this.indicator.removeClass('invisible');
29952             this.indicator.addClass('visible');
29953         }
29954         
29955         this.el.removeClass(this.validClass);
29956         
29957         this.el.addClass(this.invalidClass);
29958         
29959         this.fireEvent('invalid', this, msg);
29960     }
29961     
29962    
29963 });
29964
29965 Roo.apply(Roo.bootstrap.FieldLabel, {
29966     
29967     groups: {},
29968     
29969      /**
29970     * register a FieldLabel Group
29971     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29972     */
29973     register : function(label)
29974     {
29975         if(this.groups.hasOwnProperty(label.target)){
29976             return;
29977         }
29978      
29979         this.groups[label.target] = label;
29980         
29981     },
29982     /**
29983     * fetch a FieldLabel Group based on the target
29984     * @param {string} target
29985     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29986     */
29987     get: function(target) {
29988         if (typeof(this.groups[target]) == 'undefined') {
29989             return false;
29990         }
29991         
29992         return this.groups[target] ;
29993     }
29994 });
29995
29996  
29997
29998  /*
29999  * - LGPL
30000  *
30001  * page DateSplitField.
30002  * 
30003  */
30004
30005
30006 /**
30007  * @class Roo.bootstrap.DateSplitField
30008  * @extends Roo.bootstrap.Component
30009  * Bootstrap DateSplitField class
30010  * @cfg {string} fieldLabel - the label associated
30011  * @cfg {Number} labelWidth set the width of label (0-12)
30012  * @cfg {String} labelAlign (top|left)
30013  * @cfg {Boolean} dayAllowBlank (true|false) default false
30014  * @cfg {Boolean} monthAllowBlank (true|false) default false
30015  * @cfg {Boolean} yearAllowBlank (true|false) default false
30016  * @cfg {string} dayPlaceholder 
30017  * @cfg {string} monthPlaceholder
30018  * @cfg {string} yearPlaceholder
30019  * @cfg {string} dayFormat default 'd'
30020  * @cfg {string} monthFormat default 'm'
30021  * @cfg {string} yearFormat default 'Y'
30022  * @cfg {Number} labellg set the width of label (1-12)
30023  * @cfg {Number} labelmd set the width of label (1-12)
30024  * @cfg {Number} labelsm set the width of label (1-12)
30025  * @cfg {Number} labelxs set the width of label (1-12)
30026
30027  *     
30028  * @constructor
30029  * Create a new DateSplitField
30030  * @param {Object} config The config object
30031  */
30032
30033 Roo.bootstrap.DateSplitField = function(config){
30034     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30035     
30036     this.addEvents({
30037         // raw events
30038          /**
30039          * @event years
30040          * getting the data of years
30041          * @param {Roo.bootstrap.DateSplitField} this
30042          * @param {Object} years
30043          */
30044         "years" : true,
30045         /**
30046          * @event days
30047          * getting the data of days
30048          * @param {Roo.bootstrap.DateSplitField} this
30049          * @param {Object} days
30050          */
30051         "days" : true,
30052         /**
30053          * @event invalid
30054          * Fires after the field has been marked as invalid.
30055          * @param {Roo.form.Field} this
30056          * @param {String} msg The validation message
30057          */
30058         invalid : true,
30059        /**
30060          * @event valid
30061          * Fires after the field has been validated with no errors.
30062          * @param {Roo.form.Field} this
30063          */
30064         valid : true
30065     });
30066 };
30067
30068 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30069     
30070     fieldLabel : '',
30071     labelAlign : 'top',
30072     labelWidth : 3,
30073     dayAllowBlank : false,
30074     monthAllowBlank : false,
30075     yearAllowBlank : false,
30076     dayPlaceholder : '',
30077     monthPlaceholder : '',
30078     yearPlaceholder : '',
30079     dayFormat : 'd',
30080     monthFormat : 'm',
30081     yearFormat : 'Y',
30082     isFormField : true,
30083     labellg : 0,
30084     labelmd : 0,
30085     labelsm : 0,
30086     labelxs : 0,
30087     
30088     getAutoCreate : function()
30089     {
30090         var cfg = {
30091             tag : 'div',
30092             cls : 'row roo-date-split-field-group',
30093             cn : [
30094                 {
30095                     tag : 'input',
30096                     type : 'hidden',
30097                     cls : 'form-hidden-field roo-date-split-field-group-value',
30098                     name : this.name
30099                 }
30100             ]
30101         };
30102         
30103         var labelCls = 'col-md-12';
30104         var contentCls = 'col-md-4';
30105         
30106         if(this.fieldLabel){
30107             
30108             var label = {
30109                 tag : 'div',
30110                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30111                 cn : [
30112                     {
30113                         tag : 'label',
30114                         html : this.fieldLabel
30115                     }
30116                 ]
30117             };
30118             
30119             if(this.labelAlign == 'left'){
30120             
30121                 if(this.labelWidth > 12){
30122                     label.style = "width: " + this.labelWidth + 'px';
30123                 }
30124
30125                 if(this.labelWidth < 13 && this.labelmd == 0){
30126                     this.labelmd = this.labelWidth;
30127                 }
30128
30129                 if(this.labellg > 0){
30130                     labelCls = ' col-lg-' + this.labellg;
30131                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30132                 }
30133
30134                 if(this.labelmd > 0){
30135                     labelCls = ' col-md-' + this.labelmd;
30136                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30137                 }
30138
30139                 if(this.labelsm > 0){
30140                     labelCls = ' col-sm-' + this.labelsm;
30141                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30142                 }
30143
30144                 if(this.labelxs > 0){
30145                     labelCls = ' col-xs-' + this.labelxs;
30146                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30147                 }
30148             }
30149             
30150             label.cls += ' ' + labelCls;
30151             
30152             cfg.cn.push(label);
30153         }
30154         
30155         Roo.each(['day', 'month', 'year'], function(t){
30156             cfg.cn.push({
30157                 tag : 'div',
30158                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30159             });
30160         }, this);
30161         
30162         return cfg;
30163     },
30164     
30165     inputEl: function ()
30166     {
30167         return this.el.select('.roo-date-split-field-group-value', true).first();
30168     },
30169     
30170     onRender : function(ct, position) 
30171     {
30172         var _this = this;
30173         
30174         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30175         
30176         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30177         
30178         this.dayField = new Roo.bootstrap.ComboBox({
30179             allowBlank : this.dayAllowBlank,
30180             alwaysQuery : true,
30181             displayField : 'value',
30182             editable : false,
30183             fieldLabel : '',
30184             forceSelection : true,
30185             mode : 'local',
30186             placeholder : this.dayPlaceholder,
30187             selectOnFocus : true,
30188             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30189             triggerAction : 'all',
30190             typeAhead : true,
30191             valueField : 'value',
30192             store : new Roo.data.SimpleStore({
30193                 data : (function() {    
30194                     var days = [];
30195                     _this.fireEvent('days', _this, days);
30196                     return days;
30197                 })(),
30198                 fields : [ 'value' ]
30199             }),
30200             listeners : {
30201                 select : function (_self, record, index)
30202                 {
30203                     _this.setValue(_this.getValue());
30204                 }
30205             }
30206         });
30207
30208         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30209         
30210         this.monthField = new Roo.bootstrap.MonthField({
30211             after : '<i class=\"fa fa-calendar\"></i>',
30212             allowBlank : this.monthAllowBlank,
30213             placeholder : this.monthPlaceholder,
30214             readOnly : true,
30215             listeners : {
30216                 render : function (_self)
30217                 {
30218                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30219                         e.preventDefault();
30220                         _self.focus();
30221                     });
30222                 },
30223                 select : function (_self, oldvalue, newvalue)
30224                 {
30225                     _this.setValue(_this.getValue());
30226                 }
30227             }
30228         });
30229         
30230         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30231         
30232         this.yearField = new Roo.bootstrap.ComboBox({
30233             allowBlank : this.yearAllowBlank,
30234             alwaysQuery : true,
30235             displayField : 'value',
30236             editable : false,
30237             fieldLabel : '',
30238             forceSelection : true,
30239             mode : 'local',
30240             placeholder : this.yearPlaceholder,
30241             selectOnFocus : true,
30242             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30243             triggerAction : 'all',
30244             typeAhead : true,
30245             valueField : 'value',
30246             store : new Roo.data.SimpleStore({
30247                 data : (function() {
30248                     var years = [];
30249                     _this.fireEvent('years', _this, years);
30250                     return years;
30251                 })(),
30252                 fields : [ 'value' ]
30253             }),
30254             listeners : {
30255                 select : function (_self, record, index)
30256                 {
30257                     _this.setValue(_this.getValue());
30258                 }
30259             }
30260         });
30261
30262         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30263     },
30264     
30265     setValue : function(v, format)
30266     {
30267         this.inputEl.dom.value = v;
30268         
30269         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30270         
30271         var d = Date.parseDate(v, f);
30272         
30273         if(!d){
30274             this.validate();
30275             return;
30276         }
30277         
30278         this.setDay(d.format(this.dayFormat));
30279         this.setMonth(d.format(this.monthFormat));
30280         this.setYear(d.format(this.yearFormat));
30281         
30282         this.validate();
30283         
30284         return;
30285     },
30286     
30287     setDay : function(v)
30288     {
30289         this.dayField.setValue(v);
30290         this.inputEl.dom.value = this.getValue();
30291         this.validate();
30292         return;
30293     },
30294     
30295     setMonth : function(v)
30296     {
30297         this.monthField.setValue(v, true);
30298         this.inputEl.dom.value = this.getValue();
30299         this.validate();
30300         return;
30301     },
30302     
30303     setYear : function(v)
30304     {
30305         this.yearField.setValue(v);
30306         this.inputEl.dom.value = this.getValue();
30307         this.validate();
30308         return;
30309     },
30310     
30311     getDay : function()
30312     {
30313         return this.dayField.getValue();
30314     },
30315     
30316     getMonth : function()
30317     {
30318         return this.monthField.getValue();
30319     },
30320     
30321     getYear : function()
30322     {
30323         return this.yearField.getValue();
30324     },
30325     
30326     getValue : function()
30327     {
30328         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30329         
30330         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30331         
30332         return date;
30333     },
30334     
30335     reset : function()
30336     {
30337         this.setDay('');
30338         this.setMonth('');
30339         this.setYear('');
30340         this.inputEl.dom.value = '';
30341         this.validate();
30342         return;
30343     },
30344     
30345     validate : function()
30346     {
30347         var d = this.dayField.validate();
30348         var m = this.monthField.validate();
30349         var y = this.yearField.validate();
30350         
30351         var valid = true;
30352         
30353         if(
30354                 (!this.dayAllowBlank && !d) ||
30355                 (!this.monthAllowBlank && !m) ||
30356                 (!this.yearAllowBlank && !y)
30357         ){
30358             valid = false;
30359         }
30360         
30361         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30362             return valid;
30363         }
30364         
30365         if(valid){
30366             this.markValid();
30367             return valid;
30368         }
30369         
30370         this.markInvalid();
30371         
30372         return valid;
30373     },
30374     
30375     markValid : function()
30376     {
30377         
30378         var label = this.el.select('label', true).first();
30379         var icon = this.el.select('i.fa-star', true).first();
30380
30381         if(label && icon){
30382             icon.remove();
30383         }
30384         
30385         this.fireEvent('valid', this);
30386     },
30387     
30388      /**
30389      * Mark this field as invalid
30390      * @param {String} msg The validation message
30391      */
30392     markInvalid : function(msg)
30393     {
30394         
30395         var label = this.el.select('label', true).first();
30396         var icon = this.el.select('i.fa-star', true).first();
30397
30398         if(label && !icon){
30399             this.el.select('.roo-date-split-field-label', true).createChild({
30400                 tag : 'i',
30401                 cls : 'text-danger fa fa-lg fa-star',
30402                 tooltip : 'This field is required',
30403                 style : 'margin-right:5px;'
30404             }, label, true);
30405         }
30406         
30407         this.fireEvent('invalid', this, msg);
30408     },
30409     
30410     clearInvalid : function()
30411     {
30412         var label = this.el.select('label', true).first();
30413         var icon = this.el.select('i.fa-star', true).first();
30414
30415         if(label && icon){
30416             icon.remove();
30417         }
30418         
30419         this.fireEvent('valid', this);
30420     },
30421     
30422     getName: function()
30423     {
30424         return this.name;
30425     }
30426     
30427 });
30428
30429  /**
30430  *
30431  * This is based on 
30432  * http://masonry.desandro.com
30433  *
30434  * The idea is to render all the bricks based on vertical width...
30435  *
30436  * The original code extends 'outlayer' - we might need to use that....
30437  * 
30438  */
30439
30440
30441 /**
30442  * @class Roo.bootstrap.LayoutMasonry
30443  * @extends Roo.bootstrap.Component
30444  * Bootstrap Layout Masonry class
30445  * 
30446  * @constructor
30447  * Create a new Element
30448  * @param {Object} config The config object
30449  */
30450
30451 Roo.bootstrap.LayoutMasonry = function(config){
30452     
30453     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30454     
30455     this.bricks = [];
30456     
30457     Roo.bootstrap.LayoutMasonry.register(this);
30458     
30459     this.addEvents({
30460         // raw events
30461         /**
30462          * @event layout
30463          * Fire after layout the items
30464          * @param {Roo.bootstrap.LayoutMasonry} this
30465          * @param {Roo.EventObject} e
30466          */
30467         "layout" : true
30468     });
30469     
30470 };
30471
30472 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30473     
30474     /**
30475      * @cfg {Boolean} isLayoutInstant = no animation?
30476      */   
30477     isLayoutInstant : false, // needed?
30478    
30479     /**
30480      * @cfg {Number} boxWidth  width of the columns
30481      */   
30482     boxWidth : 450,
30483     
30484       /**
30485      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30486      */   
30487     boxHeight : 0,
30488     
30489     /**
30490      * @cfg {Number} padWidth padding below box..
30491      */   
30492     padWidth : 10, 
30493     
30494     /**
30495      * @cfg {Number} gutter gutter width..
30496      */   
30497     gutter : 10,
30498     
30499      /**
30500      * @cfg {Number} maxCols maximum number of columns
30501      */   
30502     
30503     maxCols: 0,
30504     
30505     /**
30506      * @cfg {Boolean} isAutoInitial defalut true
30507      */   
30508     isAutoInitial : true, 
30509     
30510     containerWidth: 0,
30511     
30512     /**
30513      * @cfg {Boolean} isHorizontal defalut false
30514      */   
30515     isHorizontal : false, 
30516
30517     currentSize : null,
30518     
30519     tag: 'div',
30520     
30521     cls: '',
30522     
30523     bricks: null, //CompositeElement
30524     
30525     cols : 1,
30526     
30527     _isLayoutInited : false,
30528     
30529 //    isAlternative : false, // only use for vertical layout...
30530     
30531     /**
30532      * @cfg {Number} alternativePadWidth padding below box..
30533      */   
30534     alternativePadWidth : 50,
30535     
30536     selectedBrick : [],
30537     
30538     getAutoCreate : function(){
30539         
30540         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30541         
30542         var cfg = {
30543             tag: this.tag,
30544             cls: 'blog-masonary-wrapper ' + this.cls,
30545             cn : {
30546                 cls : 'mas-boxes masonary'
30547             }
30548         };
30549         
30550         return cfg;
30551     },
30552     
30553     getChildContainer: function( )
30554     {
30555         if (this.boxesEl) {
30556             return this.boxesEl;
30557         }
30558         
30559         this.boxesEl = this.el.select('.mas-boxes').first();
30560         
30561         return this.boxesEl;
30562     },
30563     
30564     
30565     initEvents : function()
30566     {
30567         var _this = this;
30568         
30569         if(this.isAutoInitial){
30570             Roo.log('hook children rendered');
30571             this.on('childrenrendered', function() {
30572                 Roo.log('children rendered');
30573                 _this.initial();
30574             } ,this);
30575         }
30576     },
30577     
30578     initial : function()
30579     {
30580         this.selectedBrick = [];
30581         
30582         this.currentSize = this.el.getBox(true);
30583         
30584         Roo.EventManager.onWindowResize(this.resize, this); 
30585
30586         if(!this.isAutoInitial){
30587             this.layout();
30588             return;
30589         }
30590         
30591         this.layout();
30592         
30593         return;
30594         //this.layout.defer(500,this);
30595         
30596     },
30597     
30598     resize : function()
30599     {
30600         var cs = this.el.getBox(true);
30601         
30602         if (
30603                 this.currentSize.width == cs.width && 
30604                 this.currentSize.x == cs.x && 
30605                 this.currentSize.height == cs.height && 
30606                 this.currentSize.y == cs.y 
30607         ) {
30608             Roo.log("no change in with or X or Y");
30609             return;
30610         }
30611         
30612         this.currentSize = cs;
30613         
30614         this.layout();
30615         
30616     },
30617     
30618     layout : function()
30619     {   
30620         this._resetLayout();
30621         
30622         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30623         
30624         this.layoutItems( isInstant );
30625       
30626         this._isLayoutInited = true;
30627         
30628         this.fireEvent('layout', this);
30629         
30630     },
30631     
30632     _resetLayout : function()
30633     {
30634         if(this.isHorizontal){
30635             this.horizontalMeasureColumns();
30636             return;
30637         }
30638         
30639         this.verticalMeasureColumns();
30640         
30641     },
30642     
30643     verticalMeasureColumns : function()
30644     {
30645         this.getContainerWidth();
30646         
30647 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30648 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30649 //            return;
30650 //        }
30651         
30652         var boxWidth = this.boxWidth + this.padWidth;
30653         
30654         if(this.containerWidth < this.boxWidth){
30655             boxWidth = this.containerWidth
30656         }
30657         
30658         var containerWidth = this.containerWidth;
30659         
30660         var cols = Math.floor(containerWidth / boxWidth);
30661         
30662         this.cols = Math.max( cols, 1 );
30663         
30664         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30665         
30666         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30667         
30668         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30669         
30670         this.colWidth = boxWidth + avail - this.padWidth;
30671         
30672         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30673         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30674     },
30675     
30676     horizontalMeasureColumns : function()
30677     {
30678         this.getContainerWidth();
30679         
30680         var boxWidth = this.boxWidth;
30681         
30682         if(this.containerWidth < boxWidth){
30683             boxWidth = this.containerWidth;
30684         }
30685         
30686         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30687         
30688         this.el.setHeight(boxWidth);
30689         
30690     },
30691     
30692     getContainerWidth : function()
30693     {
30694         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30695     },
30696     
30697     layoutItems : function( isInstant )
30698     {
30699         Roo.log(this.bricks);
30700         
30701         var items = Roo.apply([], this.bricks);
30702         
30703         if(this.isHorizontal){
30704             this._horizontalLayoutItems( items , isInstant );
30705             return;
30706         }
30707         
30708 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30709 //            this._verticalAlternativeLayoutItems( items , isInstant );
30710 //            return;
30711 //        }
30712         
30713         this._verticalLayoutItems( items , isInstant );
30714         
30715     },
30716     
30717     _verticalLayoutItems : function ( items , isInstant)
30718     {
30719         if ( !items || !items.length ) {
30720             return;
30721         }
30722         
30723         var standard = [
30724             ['xs', 'xs', 'xs', 'tall'],
30725             ['xs', 'xs', 'tall'],
30726             ['xs', 'xs', 'sm'],
30727             ['xs', 'xs', 'xs'],
30728             ['xs', 'tall'],
30729             ['xs', 'sm'],
30730             ['xs', 'xs'],
30731             ['xs'],
30732             
30733             ['sm', 'xs', 'xs'],
30734             ['sm', 'xs'],
30735             ['sm'],
30736             
30737             ['tall', 'xs', 'xs', 'xs'],
30738             ['tall', 'xs', 'xs'],
30739             ['tall', 'xs'],
30740             ['tall']
30741             
30742         ];
30743         
30744         var queue = [];
30745         
30746         var boxes = [];
30747         
30748         var box = [];
30749         
30750         Roo.each(items, function(item, k){
30751             
30752             switch (item.size) {
30753                 // these layouts take up a full box,
30754                 case 'md' :
30755                 case 'md-left' :
30756                 case 'md-right' :
30757                 case 'wide' :
30758                     
30759                     if(box.length){
30760                         boxes.push(box);
30761                         box = [];
30762                     }
30763                     
30764                     boxes.push([item]);
30765                     
30766                     break;
30767                     
30768                 case 'xs' :
30769                 case 'sm' :
30770                 case 'tall' :
30771                     
30772                     box.push(item);
30773                     
30774                     break;
30775                 default :
30776                     break;
30777                     
30778             }
30779             
30780         }, this);
30781         
30782         if(box.length){
30783             boxes.push(box);
30784             box = [];
30785         }
30786         
30787         var filterPattern = function(box, length)
30788         {
30789             if(!box.length){
30790                 return;
30791             }
30792             
30793             var match = false;
30794             
30795             var pattern = box.slice(0, length);
30796             
30797             var format = [];
30798             
30799             Roo.each(pattern, function(i){
30800                 format.push(i.size);
30801             }, this);
30802             
30803             Roo.each(standard, function(s){
30804                 
30805                 if(String(s) != String(format)){
30806                     return;
30807                 }
30808                 
30809                 match = true;
30810                 return false;
30811                 
30812             }, this);
30813             
30814             if(!match && length == 1){
30815                 return;
30816             }
30817             
30818             if(!match){
30819                 filterPattern(box, length - 1);
30820                 return;
30821             }
30822                 
30823             queue.push(pattern);
30824
30825             box = box.slice(length, box.length);
30826
30827             filterPattern(box, 4);
30828
30829             return;
30830             
30831         }
30832         
30833         Roo.each(boxes, function(box, k){
30834             
30835             if(!box.length){
30836                 return;
30837             }
30838             
30839             if(box.length == 1){
30840                 queue.push(box);
30841                 return;
30842             }
30843             
30844             filterPattern(box, 4);
30845             
30846         }, this);
30847         
30848         this._processVerticalLayoutQueue( queue, isInstant );
30849         
30850     },
30851     
30852 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30853 //    {
30854 //        if ( !items || !items.length ) {
30855 //            return;
30856 //        }
30857 //
30858 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30859 //        
30860 //    },
30861     
30862     _horizontalLayoutItems : function ( items , isInstant)
30863     {
30864         if ( !items || !items.length || items.length < 3) {
30865             return;
30866         }
30867         
30868         items.reverse();
30869         
30870         var eItems = items.slice(0, 3);
30871         
30872         items = items.slice(3, items.length);
30873         
30874         var standard = [
30875             ['xs', 'xs', 'xs', 'wide'],
30876             ['xs', 'xs', 'wide'],
30877             ['xs', 'xs', 'sm'],
30878             ['xs', 'xs', 'xs'],
30879             ['xs', 'wide'],
30880             ['xs', 'sm'],
30881             ['xs', 'xs'],
30882             ['xs'],
30883             
30884             ['sm', 'xs', 'xs'],
30885             ['sm', 'xs'],
30886             ['sm'],
30887             
30888             ['wide', 'xs', 'xs', 'xs'],
30889             ['wide', 'xs', 'xs'],
30890             ['wide', 'xs'],
30891             ['wide'],
30892             
30893             ['wide-thin']
30894         ];
30895         
30896         var queue = [];
30897         
30898         var boxes = [];
30899         
30900         var box = [];
30901         
30902         Roo.each(items, function(item, k){
30903             
30904             switch (item.size) {
30905                 case 'md' :
30906                 case 'md-left' :
30907                 case 'md-right' :
30908                 case 'tall' :
30909                     
30910                     if(box.length){
30911                         boxes.push(box);
30912                         box = [];
30913                     }
30914                     
30915                     boxes.push([item]);
30916                     
30917                     break;
30918                     
30919                 case 'xs' :
30920                 case 'sm' :
30921                 case 'wide' :
30922                 case 'wide-thin' :
30923                     
30924                     box.push(item);
30925                     
30926                     break;
30927                 default :
30928                     break;
30929                     
30930             }
30931             
30932         }, this);
30933         
30934         if(box.length){
30935             boxes.push(box);
30936             box = [];
30937         }
30938         
30939         var filterPattern = function(box, length)
30940         {
30941             if(!box.length){
30942                 return;
30943             }
30944             
30945             var match = false;
30946             
30947             var pattern = box.slice(0, length);
30948             
30949             var format = [];
30950             
30951             Roo.each(pattern, function(i){
30952                 format.push(i.size);
30953             }, this);
30954             
30955             Roo.each(standard, function(s){
30956                 
30957                 if(String(s) != String(format)){
30958                     return;
30959                 }
30960                 
30961                 match = true;
30962                 return false;
30963                 
30964             }, this);
30965             
30966             if(!match && length == 1){
30967                 return;
30968             }
30969             
30970             if(!match){
30971                 filterPattern(box, length - 1);
30972                 return;
30973             }
30974                 
30975             queue.push(pattern);
30976
30977             box = box.slice(length, box.length);
30978
30979             filterPattern(box, 4);
30980
30981             return;
30982             
30983         }
30984         
30985         Roo.each(boxes, function(box, k){
30986             
30987             if(!box.length){
30988                 return;
30989             }
30990             
30991             if(box.length == 1){
30992                 queue.push(box);
30993                 return;
30994             }
30995             
30996             filterPattern(box, 4);
30997             
30998         }, this);
30999         
31000         
31001         var prune = [];
31002         
31003         var pos = this.el.getBox(true);
31004         
31005         var minX = pos.x;
31006         
31007         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31008         
31009         var hit_end = false;
31010         
31011         Roo.each(queue, function(box){
31012             
31013             if(hit_end){
31014                 
31015                 Roo.each(box, function(b){
31016                 
31017                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31018                     b.el.hide();
31019
31020                 }, this);
31021
31022                 return;
31023             }
31024             
31025             var mx = 0;
31026             
31027             Roo.each(box, function(b){
31028                 
31029                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31030                 b.el.show();
31031
31032                 mx = Math.max(mx, b.x);
31033                 
31034             }, this);
31035             
31036             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31037             
31038             if(maxX < minX){
31039                 
31040                 Roo.each(box, function(b){
31041                 
31042                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31043                     b.el.hide();
31044                     
31045                 }, this);
31046                 
31047                 hit_end = true;
31048                 
31049                 return;
31050             }
31051             
31052             prune.push(box);
31053             
31054         }, this);
31055         
31056         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31057     },
31058     
31059     /** Sets position of item in DOM
31060     * @param {Element} item
31061     * @param {Number} x - horizontal position
31062     * @param {Number} y - vertical position
31063     * @param {Boolean} isInstant - disables transitions
31064     */
31065     _processVerticalLayoutQueue : function( queue, isInstant )
31066     {
31067         var pos = this.el.getBox(true);
31068         var x = pos.x;
31069         var y = pos.y;
31070         var maxY = [];
31071         
31072         for (var i = 0; i < this.cols; i++){
31073             maxY[i] = pos.y;
31074         }
31075         
31076         Roo.each(queue, function(box, k){
31077             
31078             var col = k % this.cols;
31079             
31080             Roo.each(box, function(b,kk){
31081                 
31082                 b.el.position('absolute');
31083                 
31084                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31085                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31086                 
31087                 if(b.size == 'md-left' || b.size == 'md-right'){
31088                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31089                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31090                 }
31091                 
31092                 b.el.setWidth(width);
31093                 b.el.setHeight(height);
31094                 // iframe?
31095                 b.el.select('iframe',true).setSize(width,height);
31096                 
31097             }, this);
31098             
31099             for (var i = 0; i < this.cols; i++){
31100                 
31101                 if(maxY[i] < maxY[col]){
31102                     col = i;
31103                     continue;
31104                 }
31105                 
31106                 col = Math.min(col, i);
31107                 
31108             }
31109             
31110             x = pos.x + col * (this.colWidth + this.padWidth);
31111             
31112             y = maxY[col];
31113             
31114             var positions = [];
31115             
31116             switch (box.length){
31117                 case 1 :
31118                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31119                     break;
31120                 case 2 :
31121                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31122                     break;
31123                 case 3 :
31124                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31125                     break;
31126                 case 4 :
31127                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31128                     break;
31129                 default :
31130                     break;
31131             }
31132             
31133             Roo.each(box, function(b,kk){
31134                 
31135                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31136                 
31137                 var sz = b.el.getSize();
31138                 
31139                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31140                 
31141             }, this);
31142             
31143         }, this);
31144         
31145         var mY = 0;
31146         
31147         for (var i = 0; i < this.cols; i++){
31148             mY = Math.max(mY, maxY[i]);
31149         }
31150         
31151         this.el.setHeight(mY - pos.y);
31152         
31153     },
31154     
31155 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31156 //    {
31157 //        var pos = this.el.getBox(true);
31158 //        var x = pos.x;
31159 //        var y = pos.y;
31160 //        var maxX = pos.right;
31161 //        
31162 //        var maxHeight = 0;
31163 //        
31164 //        Roo.each(items, function(item, k){
31165 //            
31166 //            var c = k % 2;
31167 //            
31168 //            item.el.position('absolute');
31169 //                
31170 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31171 //
31172 //            item.el.setWidth(width);
31173 //
31174 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31175 //
31176 //            item.el.setHeight(height);
31177 //            
31178 //            if(c == 0){
31179 //                item.el.setXY([x, y], isInstant ? false : true);
31180 //            } else {
31181 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31182 //            }
31183 //            
31184 //            y = y + height + this.alternativePadWidth;
31185 //            
31186 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31187 //            
31188 //        }, this);
31189 //        
31190 //        this.el.setHeight(maxHeight);
31191 //        
31192 //    },
31193     
31194     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31195     {
31196         var pos = this.el.getBox(true);
31197         
31198         var minX = pos.x;
31199         var minY = pos.y;
31200         
31201         var maxX = pos.right;
31202         
31203         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31204         
31205         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31206         
31207         Roo.each(queue, function(box, k){
31208             
31209             Roo.each(box, function(b, kk){
31210                 
31211                 b.el.position('absolute');
31212                 
31213                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31214                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31215                 
31216                 if(b.size == 'md-left' || b.size == 'md-right'){
31217                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31218                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31219                 }
31220                 
31221                 b.el.setWidth(width);
31222                 b.el.setHeight(height);
31223                 
31224             }, this);
31225             
31226             if(!box.length){
31227                 return;
31228             }
31229             
31230             var positions = [];
31231             
31232             switch (box.length){
31233                 case 1 :
31234                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31235                     break;
31236                 case 2 :
31237                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31238                     break;
31239                 case 3 :
31240                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31241                     break;
31242                 case 4 :
31243                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31244                     break;
31245                 default :
31246                     break;
31247             }
31248             
31249             Roo.each(box, function(b,kk){
31250                 
31251                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31252                 
31253                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31254                 
31255             }, this);
31256             
31257         }, this);
31258         
31259     },
31260     
31261     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31262     {
31263         Roo.each(eItems, function(b,k){
31264             
31265             b.size = (k == 0) ? 'sm' : 'xs';
31266             b.x = (k == 0) ? 2 : 1;
31267             b.y = (k == 0) ? 2 : 1;
31268             
31269             b.el.position('absolute');
31270             
31271             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31272                 
31273             b.el.setWidth(width);
31274             
31275             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31276             
31277             b.el.setHeight(height);
31278             
31279         }, this);
31280
31281         var positions = [];
31282         
31283         positions.push({
31284             x : maxX - this.unitWidth * 2 - this.gutter,
31285             y : minY
31286         });
31287         
31288         positions.push({
31289             x : maxX - this.unitWidth,
31290             y : minY + (this.unitWidth + this.gutter) * 2
31291         });
31292         
31293         positions.push({
31294             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31295             y : minY
31296         });
31297         
31298         Roo.each(eItems, function(b,k){
31299             
31300             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31301
31302         }, this);
31303         
31304     },
31305     
31306     getVerticalOneBoxColPositions : function(x, y, box)
31307     {
31308         var pos = [];
31309         
31310         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31311         
31312         if(box[0].size == 'md-left'){
31313             rand = 0;
31314         }
31315         
31316         if(box[0].size == 'md-right'){
31317             rand = 1;
31318         }
31319         
31320         pos.push({
31321             x : x + (this.unitWidth + this.gutter) * rand,
31322             y : y
31323         });
31324         
31325         return pos;
31326     },
31327     
31328     getVerticalTwoBoxColPositions : function(x, y, box)
31329     {
31330         var pos = [];
31331         
31332         if(box[0].size == 'xs'){
31333             
31334             pos.push({
31335                 x : x,
31336                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31337             });
31338
31339             pos.push({
31340                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31341                 y : y
31342             });
31343             
31344             return pos;
31345             
31346         }
31347         
31348         pos.push({
31349             x : x,
31350             y : y
31351         });
31352
31353         pos.push({
31354             x : x + (this.unitWidth + this.gutter) * 2,
31355             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31356         });
31357         
31358         return pos;
31359         
31360     },
31361     
31362     getVerticalThreeBoxColPositions : function(x, y, box)
31363     {
31364         var pos = [];
31365         
31366         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31367             
31368             pos.push({
31369                 x : x,
31370                 y : y
31371             });
31372
31373             pos.push({
31374                 x : x + (this.unitWidth + this.gutter) * 1,
31375                 y : y
31376             });
31377             
31378             pos.push({
31379                 x : x + (this.unitWidth + this.gutter) * 2,
31380                 y : y
31381             });
31382             
31383             return pos;
31384             
31385         }
31386         
31387         if(box[0].size == 'xs' && box[1].size == 'xs'){
31388             
31389             pos.push({
31390                 x : x,
31391                 y : y
31392             });
31393
31394             pos.push({
31395                 x : x,
31396                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31397             });
31398             
31399             pos.push({
31400                 x : x + (this.unitWidth + this.gutter) * 1,
31401                 y : y
31402             });
31403             
31404             return pos;
31405             
31406         }
31407         
31408         pos.push({
31409             x : x,
31410             y : y
31411         });
31412
31413         pos.push({
31414             x : x + (this.unitWidth + this.gutter) * 2,
31415             y : y
31416         });
31417
31418         pos.push({
31419             x : x + (this.unitWidth + this.gutter) * 2,
31420             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31421         });
31422             
31423         return pos;
31424         
31425     },
31426     
31427     getVerticalFourBoxColPositions : function(x, y, box)
31428     {
31429         var pos = [];
31430         
31431         if(box[0].size == 'xs'){
31432             
31433             pos.push({
31434                 x : x,
31435                 y : y
31436             });
31437
31438             pos.push({
31439                 x : x,
31440                 y : y + (this.unitHeight + this.gutter) * 1
31441             });
31442             
31443             pos.push({
31444                 x : x,
31445                 y : y + (this.unitHeight + this.gutter) * 2
31446             });
31447             
31448             pos.push({
31449                 x : x + (this.unitWidth + this.gutter) * 1,
31450                 y : y
31451             });
31452             
31453             return pos;
31454             
31455         }
31456         
31457         pos.push({
31458             x : x,
31459             y : y
31460         });
31461
31462         pos.push({
31463             x : x + (this.unitWidth + this.gutter) * 2,
31464             y : y
31465         });
31466
31467         pos.push({
31468             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31469             y : y + (this.unitHeight + this.gutter) * 1
31470         });
31471
31472         pos.push({
31473             x : x + (this.unitWidth + this.gutter) * 2,
31474             y : y + (this.unitWidth + this.gutter) * 2
31475         });
31476
31477         return pos;
31478         
31479     },
31480     
31481     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31482     {
31483         var pos = [];
31484         
31485         if(box[0].size == 'md-left'){
31486             pos.push({
31487                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31488                 y : minY
31489             });
31490             
31491             return pos;
31492         }
31493         
31494         if(box[0].size == 'md-right'){
31495             pos.push({
31496                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31497                 y : minY + (this.unitWidth + this.gutter) * 1
31498             });
31499             
31500             return pos;
31501         }
31502         
31503         var rand = Math.floor(Math.random() * (4 - box[0].y));
31504         
31505         pos.push({
31506             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31507             y : minY + (this.unitWidth + this.gutter) * rand
31508         });
31509         
31510         return pos;
31511         
31512     },
31513     
31514     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31515     {
31516         var pos = [];
31517         
31518         if(box[0].size == 'xs'){
31519             
31520             pos.push({
31521                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31522                 y : minY
31523             });
31524
31525             pos.push({
31526                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31527                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31528             });
31529             
31530             return pos;
31531             
31532         }
31533         
31534         pos.push({
31535             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31536             y : minY
31537         });
31538
31539         pos.push({
31540             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31541             y : minY + (this.unitWidth + this.gutter) * 2
31542         });
31543         
31544         return pos;
31545         
31546     },
31547     
31548     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31549     {
31550         var pos = [];
31551         
31552         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31553             
31554             pos.push({
31555                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31556                 y : minY
31557             });
31558
31559             pos.push({
31560                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31561                 y : minY + (this.unitWidth + this.gutter) * 1
31562             });
31563             
31564             pos.push({
31565                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31566                 y : minY + (this.unitWidth + this.gutter) * 2
31567             });
31568             
31569             return pos;
31570             
31571         }
31572         
31573         if(box[0].size == 'xs' && box[1].size == 'xs'){
31574             
31575             pos.push({
31576                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31577                 y : minY
31578             });
31579
31580             pos.push({
31581                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31582                 y : minY
31583             });
31584             
31585             pos.push({
31586                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31587                 y : minY + (this.unitWidth + this.gutter) * 1
31588             });
31589             
31590             return pos;
31591             
31592         }
31593         
31594         pos.push({
31595             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31596             y : minY
31597         });
31598
31599         pos.push({
31600             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31601             y : minY + (this.unitWidth + this.gutter) * 2
31602         });
31603
31604         pos.push({
31605             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31606             y : minY + (this.unitWidth + this.gutter) * 2
31607         });
31608             
31609         return pos;
31610         
31611     },
31612     
31613     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31614     {
31615         var pos = [];
31616         
31617         if(box[0].size == 'xs'){
31618             
31619             pos.push({
31620                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31621                 y : minY
31622             });
31623
31624             pos.push({
31625                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31626                 y : minY
31627             });
31628             
31629             pos.push({
31630                 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),
31631                 y : minY
31632             });
31633             
31634             pos.push({
31635                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31636                 y : minY + (this.unitWidth + this.gutter) * 1
31637             });
31638             
31639             return pos;
31640             
31641         }
31642         
31643         pos.push({
31644             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31645             y : minY
31646         });
31647         
31648         pos.push({
31649             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31650             y : minY + (this.unitWidth + this.gutter) * 2
31651         });
31652         
31653         pos.push({
31654             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31655             y : minY + (this.unitWidth + this.gutter) * 2
31656         });
31657         
31658         pos.push({
31659             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),
31660             y : minY + (this.unitWidth + this.gutter) * 2
31661         });
31662
31663         return pos;
31664         
31665     },
31666     
31667     /**
31668     * remove a Masonry Brick
31669     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31670     */
31671     removeBrick : function(brick_id)
31672     {
31673         if (!brick_id) {
31674             return;
31675         }
31676         
31677         for (var i = 0; i<this.bricks.length; i++) {
31678             if (this.bricks[i].id == brick_id) {
31679                 this.bricks.splice(i,1);
31680                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31681                 this.initial();
31682             }
31683         }
31684     },
31685     
31686     /**
31687     * adds a Masonry Brick
31688     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31689     */
31690     addBrick : function(cfg)
31691     {
31692         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31693         //this.register(cn);
31694         cn.parentId = this.id;
31695         cn.onRender(this.el, null);
31696         return cn;
31697     },
31698     
31699     /**
31700     * register a Masonry Brick
31701     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31702     */
31703     
31704     register : function(brick)
31705     {
31706         this.bricks.push(brick);
31707         brick.masonryId = this.id;
31708     },
31709     
31710     /**
31711     * clear all the Masonry Brick
31712     */
31713     clearAll : function()
31714     {
31715         this.bricks = [];
31716         //this.getChildContainer().dom.innerHTML = "";
31717         this.el.dom.innerHTML = '';
31718     },
31719     
31720     getSelected : function()
31721     {
31722         if (!this.selectedBrick) {
31723             return false;
31724         }
31725         
31726         return this.selectedBrick;
31727     }
31728 });
31729
31730 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31731     
31732     groups: {},
31733      /**
31734     * register a Masonry Layout
31735     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31736     */
31737     
31738     register : function(layout)
31739     {
31740         this.groups[layout.id] = layout;
31741     },
31742     /**
31743     * fetch a  Masonry Layout based on the masonry layout ID
31744     * @param {string} the masonry layout to add
31745     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31746     */
31747     
31748     get: function(layout_id) {
31749         if (typeof(this.groups[layout_id]) == 'undefined') {
31750             return false;
31751         }
31752         return this.groups[layout_id] ;
31753     }
31754     
31755     
31756     
31757 });
31758
31759  
31760
31761  /**
31762  *
31763  * This is based on 
31764  * http://masonry.desandro.com
31765  *
31766  * The idea is to render all the bricks based on vertical width...
31767  *
31768  * The original code extends 'outlayer' - we might need to use that....
31769  * 
31770  */
31771
31772
31773 /**
31774  * @class Roo.bootstrap.LayoutMasonryAuto
31775  * @extends Roo.bootstrap.Component
31776  * Bootstrap Layout Masonry class
31777  * 
31778  * @constructor
31779  * Create a new Element
31780  * @param {Object} config The config object
31781  */
31782
31783 Roo.bootstrap.LayoutMasonryAuto = function(config){
31784     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31785 };
31786
31787 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31788     
31789       /**
31790      * @cfg {Boolean} isFitWidth  - resize the width..
31791      */   
31792     isFitWidth : false,  // options..
31793     /**
31794      * @cfg {Boolean} isOriginLeft = left align?
31795      */   
31796     isOriginLeft : true,
31797     /**
31798      * @cfg {Boolean} isOriginTop = top align?
31799      */   
31800     isOriginTop : false,
31801     /**
31802      * @cfg {Boolean} isLayoutInstant = no animation?
31803      */   
31804     isLayoutInstant : false, // needed?
31805     /**
31806      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31807      */   
31808     isResizingContainer : true,
31809     /**
31810      * @cfg {Number} columnWidth  width of the columns 
31811      */   
31812     
31813     columnWidth : 0,
31814     
31815     /**
31816      * @cfg {Number} maxCols maximum number of columns
31817      */   
31818     
31819     maxCols: 0,
31820     /**
31821      * @cfg {Number} padHeight padding below box..
31822      */   
31823     
31824     padHeight : 10, 
31825     
31826     /**
31827      * @cfg {Boolean} isAutoInitial defalut true
31828      */   
31829     
31830     isAutoInitial : true, 
31831     
31832     // private?
31833     gutter : 0,
31834     
31835     containerWidth: 0,
31836     initialColumnWidth : 0,
31837     currentSize : null,
31838     
31839     colYs : null, // array.
31840     maxY : 0,
31841     padWidth: 10,
31842     
31843     
31844     tag: 'div',
31845     cls: '',
31846     bricks: null, //CompositeElement
31847     cols : 0, // array?
31848     // element : null, // wrapped now this.el
31849     _isLayoutInited : null, 
31850     
31851     
31852     getAutoCreate : function(){
31853         
31854         var cfg = {
31855             tag: this.tag,
31856             cls: 'blog-masonary-wrapper ' + this.cls,
31857             cn : {
31858                 cls : 'mas-boxes masonary'
31859             }
31860         };
31861         
31862         return cfg;
31863     },
31864     
31865     getChildContainer: function( )
31866     {
31867         if (this.boxesEl) {
31868             return this.boxesEl;
31869         }
31870         
31871         this.boxesEl = this.el.select('.mas-boxes').first();
31872         
31873         return this.boxesEl;
31874     },
31875     
31876     
31877     initEvents : function()
31878     {
31879         var _this = this;
31880         
31881         if(this.isAutoInitial){
31882             Roo.log('hook children rendered');
31883             this.on('childrenrendered', function() {
31884                 Roo.log('children rendered');
31885                 _this.initial();
31886             } ,this);
31887         }
31888         
31889     },
31890     
31891     initial : function()
31892     {
31893         this.reloadItems();
31894
31895         this.currentSize = this.el.getBox(true);
31896
31897         /// was window resize... - let's see if this works..
31898         Roo.EventManager.onWindowResize(this.resize, this); 
31899
31900         if(!this.isAutoInitial){
31901             this.layout();
31902             return;
31903         }
31904         
31905         this.layout.defer(500,this);
31906     },
31907     
31908     reloadItems: function()
31909     {
31910         this.bricks = this.el.select('.masonry-brick', true);
31911         
31912         this.bricks.each(function(b) {
31913             //Roo.log(b.getSize());
31914             if (!b.attr('originalwidth')) {
31915                 b.attr('originalwidth',  b.getSize().width);
31916             }
31917             
31918         });
31919         
31920         Roo.log(this.bricks.elements.length);
31921     },
31922     
31923     resize : function()
31924     {
31925         Roo.log('resize');
31926         var cs = this.el.getBox(true);
31927         
31928         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31929             Roo.log("no change in with or X");
31930             return;
31931         }
31932         this.currentSize = cs;
31933         this.layout();
31934     },
31935     
31936     layout : function()
31937     {
31938          Roo.log('layout');
31939         this._resetLayout();
31940         //this._manageStamps();
31941       
31942         // don't animate first layout
31943         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31944         this.layoutItems( isInstant );
31945       
31946         // flag for initalized
31947         this._isLayoutInited = true;
31948     },
31949     
31950     layoutItems : function( isInstant )
31951     {
31952         //var items = this._getItemsForLayout( this.items );
31953         // original code supports filtering layout items.. we just ignore it..
31954         
31955         this._layoutItems( this.bricks , isInstant );
31956       
31957         this._postLayout();
31958     },
31959     _layoutItems : function ( items , isInstant)
31960     {
31961        //this.fireEvent( 'layout', this, items );
31962     
31963
31964         if ( !items || !items.elements.length ) {
31965           // no items, emit event with empty array
31966             return;
31967         }
31968
31969         var queue = [];
31970         items.each(function(item) {
31971             Roo.log("layout item");
31972             Roo.log(item);
31973             // get x/y object from method
31974             var position = this._getItemLayoutPosition( item );
31975             // enqueue
31976             position.item = item;
31977             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31978             queue.push( position );
31979         }, this);
31980       
31981         this._processLayoutQueue( queue );
31982     },
31983     /** Sets position of item in DOM
31984     * @param {Element} item
31985     * @param {Number} x - horizontal position
31986     * @param {Number} y - vertical position
31987     * @param {Boolean} isInstant - disables transitions
31988     */
31989     _processLayoutQueue : function( queue )
31990     {
31991         for ( var i=0, len = queue.length; i < len; i++ ) {
31992             var obj = queue[i];
31993             obj.item.position('absolute');
31994             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31995         }
31996     },
31997       
31998     
31999     /**
32000     * Any logic you want to do after each layout,
32001     * i.e. size the container
32002     */
32003     _postLayout : function()
32004     {
32005         this.resizeContainer();
32006     },
32007     
32008     resizeContainer : function()
32009     {
32010         if ( !this.isResizingContainer ) {
32011             return;
32012         }
32013         var size = this._getContainerSize();
32014         if ( size ) {
32015             this.el.setSize(size.width,size.height);
32016             this.boxesEl.setSize(size.width,size.height);
32017         }
32018     },
32019     
32020     
32021     
32022     _resetLayout : function()
32023     {
32024         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32025         this.colWidth = this.el.getWidth();
32026         //this.gutter = this.el.getWidth(); 
32027         
32028         this.measureColumns();
32029
32030         // reset column Y
32031         var i = this.cols;
32032         this.colYs = [];
32033         while (i--) {
32034             this.colYs.push( 0 );
32035         }
32036     
32037         this.maxY = 0;
32038     },
32039
32040     measureColumns : function()
32041     {
32042         this.getContainerWidth();
32043       // if columnWidth is 0, default to outerWidth of first item
32044         if ( !this.columnWidth ) {
32045             var firstItem = this.bricks.first();
32046             Roo.log(firstItem);
32047             this.columnWidth  = this.containerWidth;
32048             if (firstItem && firstItem.attr('originalwidth') ) {
32049                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32050             }
32051             // columnWidth fall back to item of first element
32052             Roo.log("set column width?");
32053                         this.initialColumnWidth = this.columnWidth  ;
32054
32055             // if first elem has no width, default to size of container
32056             
32057         }
32058         
32059         
32060         if (this.initialColumnWidth) {
32061             this.columnWidth = this.initialColumnWidth;
32062         }
32063         
32064         
32065             
32066         // column width is fixed at the top - however if container width get's smaller we should
32067         // reduce it...
32068         
32069         // this bit calcs how man columns..
32070             
32071         var columnWidth = this.columnWidth += this.gutter;
32072       
32073         // calculate columns
32074         var containerWidth = this.containerWidth + this.gutter;
32075         
32076         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32077         // fix rounding errors, typically with gutters
32078         var excess = columnWidth - containerWidth % columnWidth;
32079         
32080         
32081         // if overshoot is less than a pixel, round up, otherwise floor it
32082         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32083         cols = Math[ mathMethod ]( cols );
32084         this.cols = Math.max( cols, 1 );
32085         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32086         
32087          // padding positioning..
32088         var totalColWidth = this.cols * this.columnWidth;
32089         var padavail = this.containerWidth - totalColWidth;
32090         // so for 2 columns - we need 3 'pads'
32091         
32092         var padNeeded = (1+this.cols) * this.padWidth;
32093         
32094         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32095         
32096         this.columnWidth += padExtra
32097         //this.padWidth = Math.floor(padavail /  ( this.cols));
32098         
32099         // adjust colum width so that padding is fixed??
32100         
32101         // we have 3 columns ... total = width * 3
32102         // we have X left over... that should be used by 
32103         
32104         //if (this.expandC) {
32105             
32106         //}
32107         
32108         
32109         
32110     },
32111     
32112     getContainerWidth : function()
32113     {
32114        /* // container is parent if fit width
32115         var container = this.isFitWidth ? this.element.parentNode : this.element;
32116         // check that this.size and size are there
32117         // IE8 triggers resize on body size change, so they might not be
32118         
32119         var size = getSize( container );  //FIXME
32120         this.containerWidth = size && size.innerWidth; //FIXME
32121         */
32122          
32123         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32124         
32125     },
32126     
32127     _getItemLayoutPosition : function( item )  // what is item?
32128     {
32129         // we resize the item to our columnWidth..
32130       
32131         item.setWidth(this.columnWidth);
32132         item.autoBoxAdjust  = false;
32133         
32134         var sz = item.getSize();
32135  
32136         // how many columns does this brick span
32137         var remainder = this.containerWidth % this.columnWidth;
32138         
32139         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32140         // round if off by 1 pixel, otherwise use ceil
32141         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32142         colSpan = Math.min( colSpan, this.cols );
32143         
32144         // normally this should be '1' as we dont' currently allow multi width columns..
32145         
32146         var colGroup = this._getColGroup( colSpan );
32147         // get the minimum Y value from the columns
32148         var minimumY = Math.min.apply( Math, colGroup );
32149         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32150         
32151         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32152          
32153         // position the brick
32154         var position = {
32155             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32156             y: this.currentSize.y + minimumY + this.padHeight
32157         };
32158         
32159         Roo.log(position);
32160         // apply setHeight to necessary columns
32161         var setHeight = minimumY + sz.height + this.padHeight;
32162         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32163         
32164         var setSpan = this.cols + 1 - colGroup.length;
32165         for ( var i = 0; i < setSpan; i++ ) {
32166           this.colYs[ shortColIndex + i ] = setHeight ;
32167         }
32168       
32169         return position;
32170     },
32171     
32172     /**
32173      * @param {Number} colSpan - number of columns the element spans
32174      * @returns {Array} colGroup
32175      */
32176     _getColGroup : function( colSpan )
32177     {
32178         if ( colSpan < 2 ) {
32179           // if brick spans only one column, use all the column Ys
32180           return this.colYs;
32181         }
32182       
32183         var colGroup = [];
32184         // how many different places could this brick fit horizontally
32185         var groupCount = this.cols + 1 - colSpan;
32186         // for each group potential horizontal position
32187         for ( var i = 0; i < groupCount; i++ ) {
32188           // make an array of colY values for that one group
32189           var groupColYs = this.colYs.slice( i, i + colSpan );
32190           // and get the max value of the array
32191           colGroup[i] = Math.max.apply( Math, groupColYs );
32192         }
32193         return colGroup;
32194     },
32195     /*
32196     _manageStamp : function( stamp )
32197     {
32198         var stampSize =  stamp.getSize();
32199         var offset = stamp.getBox();
32200         // get the columns that this stamp affects
32201         var firstX = this.isOriginLeft ? offset.x : offset.right;
32202         var lastX = firstX + stampSize.width;
32203         var firstCol = Math.floor( firstX / this.columnWidth );
32204         firstCol = Math.max( 0, firstCol );
32205         
32206         var lastCol = Math.floor( lastX / this.columnWidth );
32207         // lastCol should not go over if multiple of columnWidth #425
32208         lastCol -= lastX % this.columnWidth ? 0 : 1;
32209         lastCol = Math.min( this.cols - 1, lastCol );
32210         
32211         // set colYs to bottom of the stamp
32212         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32213             stampSize.height;
32214             
32215         for ( var i = firstCol; i <= lastCol; i++ ) {
32216           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32217         }
32218     },
32219     */
32220     
32221     _getContainerSize : function()
32222     {
32223         this.maxY = Math.max.apply( Math, this.colYs );
32224         var size = {
32225             height: this.maxY
32226         };
32227       
32228         if ( this.isFitWidth ) {
32229             size.width = this._getContainerFitWidth();
32230         }
32231       
32232         return size;
32233     },
32234     
32235     _getContainerFitWidth : function()
32236     {
32237         var unusedCols = 0;
32238         // count unused columns
32239         var i = this.cols;
32240         while ( --i ) {
32241           if ( this.colYs[i] !== 0 ) {
32242             break;
32243           }
32244           unusedCols++;
32245         }
32246         // fit container to columns that have been used
32247         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32248     },
32249     
32250     needsResizeLayout : function()
32251     {
32252         var previousWidth = this.containerWidth;
32253         this.getContainerWidth();
32254         return previousWidth !== this.containerWidth;
32255     }
32256  
32257 });
32258
32259  
32260
32261  /*
32262  * - LGPL
32263  *
32264  * element
32265  * 
32266  */
32267
32268 /**
32269  * @class Roo.bootstrap.MasonryBrick
32270  * @extends Roo.bootstrap.Component
32271  * Bootstrap MasonryBrick class
32272  * 
32273  * @constructor
32274  * Create a new MasonryBrick
32275  * @param {Object} config The config object
32276  */
32277
32278 Roo.bootstrap.MasonryBrick = function(config){
32279     
32280     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32281     
32282     Roo.bootstrap.MasonryBrick.register(this);
32283     
32284     this.addEvents({
32285         // raw events
32286         /**
32287          * @event click
32288          * When a MasonryBrick is clcik
32289          * @param {Roo.bootstrap.MasonryBrick} this
32290          * @param {Roo.EventObject} e
32291          */
32292         "click" : true
32293     });
32294 };
32295
32296 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32297     
32298     /**
32299      * @cfg {String} title
32300      */   
32301     title : '',
32302     /**
32303      * @cfg {String} html
32304      */   
32305     html : '',
32306     /**
32307      * @cfg {String} bgimage
32308      */   
32309     bgimage : '',
32310     /**
32311      * @cfg {String} videourl
32312      */   
32313     videourl : '',
32314     /**
32315      * @cfg {String} cls
32316      */   
32317     cls : '',
32318     /**
32319      * @cfg {String} href
32320      */   
32321     href : '',
32322     /**
32323      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32324      */   
32325     size : 'xs',
32326     
32327     /**
32328      * @cfg {String} placetitle (center|bottom)
32329      */   
32330     placetitle : '',
32331     
32332     /**
32333      * @cfg {Boolean} isFitContainer defalut true
32334      */   
32335     isFitContainer : true, 
32336     
32337     /**
32338      * @cfg {Boolean} preventDefault defalut false
32339      */   
32340     preventDefault : false, 
32341     
32342     /**
32343      * @cfg {Boolean} inverse defalut false
32344      */   
32345     maskInverse : false, 
32346     
32347     getAutoCreate : function()
32348     {
32349         if(!this.isFitContainer){
32350             return this.getSplitAutoCreate();
32351         }
32352         
32353         var cls = 'masonry-brick masonry-brick-full';
32354         
32355         if(this.href.length){
32356             cls += ' masonry-brick-link';
32357         }
32358         
32359         if(this.bgimage.length){
32360             cls += ' masonry-brick-image';
32361         }
32362         
32363         if(this.maskInverse){
32364             cls += ' mask-inverse';
32365         }
32366         
32367         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32368             cls += ' enable-mask';
32369         }
32370         
32371         if(this.size){
32372             cls += ' masonry-' + this.size + '-brick';
32373         }
32374         
32375         if(this.placetitle.length){
32376             
32377             switch (this.placetitle) {
32378                 case 'center' :
32379                     cls += ' masonry-center-title';
32380                     break;
32381                 case 'bottom' :
32382                     cls += ' masonry-bottom-title';
32383                     break;
32384                 default:
32385                     break;
32386             }
32387             
32388         } else {
32389             if(!this.html.length && !this.bgimage.length){
32390                 cls += ' masonry-center-title';
32391             }
32392
32393             if(!this.html.length && this.bgimage.length){
32394                 cls += ' masonry-bottom-title';
32395             }
32396         }
32397         
32398         if(this.cls){
32399             cls += ' ' + this.cls;
32400         }
32401         
32402         var cfg = {
32403             tag: (this.href.length) ? 'a' : 'div',
32404             cls: cls,
32405             cn: [
32406                 {
32407                     tag: 'div',
32408                     cls: 'masonry-brick-mask'
32409                 },
32410                 {
32411                     tag: 'div',
32412                     cls: 'masonry-brick-paragraph',
32413                     cn: []
32414                 }
32415             ]
32416         };
32417         
32418         if(this.href.length){
32419             cfg.href = this.href;
32420         }
32421         
32422         var cn = cfg.cn[1].cn;
32423         
32424         if(this.title.length){
32425             cn.push({
32426                 tag: 'h4',
32427                 cls: 'masonry-brick-title',
32428                 html: this.title
32429             });
32430         }
32431         
32432         if(this.html.length){
32433             cn.push({
32434                 tag: 'p',
32435                 cls: 'masonry-brick-text',
32436                 html: this.html
32437             });
32438         }
32439         
32440         if (!this.title.length && !this.html.length) {
32441             cfg.cn[1].cls += ' hide';
32442         }
32443         
32444         if(this.bgimage.length){
32445             cfg.cn.push({
32446                 tag: 'img',
32447                 cls: 'masonry-brick-image-view',
32448                 src: this.bgimage
32449             });
32450         }
32451         
32452         if(this.videourl.length){
32453             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32454             // youtube support only?
32455             cfg.cn.push({
32456                 tag: 'iframe',
32457                 cls: 'masonry-brick-image-view',
32458                 src: vurl,
32459                 frameborder : 0,
32460                 allowfullscreen : true
32461             });
32462         }
32463         
32464         return cfg;
32465         
32466     },
32467     
32468     getSplitAutoCreate : function()
32469     {
32470         var cls = 'masonry-brick masonry-brick-split';
32471         
32472         if(this.href.length){
32473             cls += ' masonry-brick-link';
32474         }
32475         
32476         if(this.bgimage.length){
32477             cls += ' masonry-brick-image';
32478         }
32479         
32480         if(this.size){
32481             cls += ' masonry-' + this.size + '-brick';
32482         }
32483         
32484         switch (this.placetitle) {
32485             case 'center' :
32486                 cls += ' masonry-center-title';
32487                 break;
32488             case 'bottom' :
32489                 cls += ' masonry-bottom-title';
32490                 break;
32491             default:
32492                 if(!this.bgimage.length){
32493                     cls += ' masonry-center-title';
32494                 }
32495
32496                 if(this.bgimage.length){
32497                     cls += ' masonry-bottom-title';
32498                 }
32499                 break;
32500         }
32501         
32502         if(this.cls){
32503             cls += ' ' + this.cls;
32504         }
32505         
32506         var cfg = {
32507             tag: (this.href.length) ? 'a' : 'div',
32508             cls: cls,
32509             cn: [
32510                 {
32511                     tag: 'div',
32512                     cls: 'masonry-brick-split-head',
32513                     cn: [
32514                         {
32515                             tag: 'div',
32516                             cls: 'masonry-brick-paragraph',
32517                             cn: []
32518                         }
32519                     ]
32520                 },
32521                 {
32522                     tag: 'div',
32523                     cls: 'masonry-brick-split-body',
32524                     cn: []
32525                 }
32526             ]
32527         };
32528         
32529         if(this.href.length){
32530             cfg.href = this.href;
32531         }
32532         
32533         if(this.title.length){
32534             cfg.cn[0].cn[0].cn.push({
32535                 tag: 'h4',
32536                 cls: 'masonry-brick-title',
32537                 html: this.title
32538             });
32539         }
32540         
32541         if(this.html.length){
32542             cfg.cn[1].cn.push({
32543                 tag: 'p',
32544                 cls: 'masonry-brick-text',
32545                 html: this.html
32546             });
32547         }
32548
32549         if(this.bgimage.length){
32550             cfg.cn[0].cn.push({
32551                 tag: 'img',
32552                 cls: 'masonry-brick-image-view',
32553                 src: this.bgimage
32554             });
32555         }
32556         
32557         if(this.videourl.length){
32558             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32559             // youtube support only?
32560             cfg.cn[0].cn.cn.push({
32561                 tag: 'iframe',
32562                 cls: 'masonry-brick-image-view',
32563                 src: vurl,
32564                 frameborder : 0,
32565                 allowfullscreen : true
32566             });
32567         }
32568         
32569         return cfg;
32570     },
32571     
32572     initEvents: function() 
32573     {
32574         switch (this.size) {
32575             case 'xs' :
32576                 this.x = 1;
32577                 this.y = 1;
32578                 break;
32579             case 'sm' :
32580                 this.x = 2;
32581                 this.y = 2;
32582                 break;
32583             case 'md' :
32584             case 'md-left' :
32585             case 'md-right' :
32586                 this.x = 3;
32587                 this.y = 3;
32588                 break;
32589             case 'tall' :
32590                 this.x = 2;
32591                 this.y = 3;
32592                 break;
32593             case 'wide' :
32594                 this.x = 3;
32595                 this.y = 2;
32596                 break;
32597             case 'wide-thin' :
32598                 this.x = 3;
32599                 this.y = 1;
32600                 break;
32601                         
32602             default :
32603                 break;
32604         }
32605         
32606         if(Roo.isTouch){
32607             this.el.on('touchstart', this.onTouchStart, this);
32608             this.el.on('touchmove', this.onTouchMove, this);
32609             this.el.on('touchend', this.onTouchEnd, this);
32610             this.el.on('contextmenu', this.onContextMenu, this);
32611         } else {
32612             this.el.on('mouseenter'  ,this.enter, this);
32613             this.el.on('mouseleave', this.leave, this);
32614             this.el.on('click', this.onClick, this);
32615         }
32616         
32617         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32618             this.parent().bricks.push(this);   
32619         }
32620         
32621     },
32622     
32623     onClick: function(e, el)
32624     {
32625         var time = this.endTimer - this.startTimer;
32626         // Roo.log(e.preventDefault());
32627         if(Roo.isTouch){
32628             if(time > 1000){
32629                 e.preventDefault();
32630                 return;
32631             }
32632         }
32633         
32634         if(!this.preventDefault){
32635             return;
32636         }
32637         
32638         e.preventDefault();
32639         
32640         if (this.activcClass != '') {
32641             this.selectBrick();
32642         }
32643         
32644         this.fireEvent('click', this);
32645     },
32646     
32647     enter: function(e, el)
32648     {
32649         e.preventDefault();
32650         
32651         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32652             return;
32653         }
32654         
32655         if(this.bgimage.length && this.html.length){
32656             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32657         }
32658     },
32659     
32660     leave: function(e, el)
32661     {
32662         e.preventDefault();
32663         
32664         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32665             return;
32666         }
32667         
32668         if(this.bgimage.length && this.html.length){
32669             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32670         }
32671     },
32672     
32673     onTouchStart: function(e, el)
32674     {
32675 //        e.preventDefault();
32676         
32677         this.touchmoved = false;
32678         
32679         if(!this.isFitContainer){
32680             return;
32681         }
32682         
32683         if(!this.bgimage.length || !this.html.length){
32684             return;
32685         }
32686         
32687         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32688         
32689         this.timer = new Date().getTime();
32690         
32691     },
32692     
32693     onTouchMove: function(e, el)
32694     {
32695         this.touchmoved = true;
32696     },
32697     
32698     onContextMenu : function(e,el)
32699     {
32700         e.preventDefault();
32701         e.stopPropagation();
32702         return false;
32703     },
32704     
32705     onTouchEnd: function(e, el)
32706     {
32707 //        e.preventDefault();
32708         
32709         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32710         
32711             this.leave(e,el);
32712             
32713             return;
32714         }
32715         
32716         if(!this.bgimage.length || !this.html.length){
32717             
32718             if(this.href.length){
32719                 window.location.href = this.href;
32720             }
32721             
32722             return;
32723         }
32724         
32725         if(!this.isFitContainer){
32726             return;
32727         }
32728         
32729         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32730         
32731         window.location.href = this.href;
32732     },
32733     
32734     //selection on single brick only
32735     selectBrick : function() {
32736         
32737         if (!this.parentId) {
32738             return;
32739         }
32740         
32741         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32742         var index = m.selectedBrick.indexOf(this.id);
32743         
32744         if ( index > -1) {
32745             m.selectedBrick.splice(index,1);
32746             this.el.removeClass(this.activeClass);
32747             return;
32748         }
32749         
32750         for(var i = 0; i < m.selectedBrick.length; i++) {
32751             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32752             b.el.removeClass(b.activeClass);
32753         }
32754         
32755         m.selectedBrick = [];
32756         
32757         m.selectedBrick.push(this.id);
32758         this.el.addClass(this.activeClass);
32759         return;
32760     }
32761     
32762 });
32763
32764 Roo.apply(Roo.bootstrap.MasonryBrick, {
32765     
32766     //groups: {},
32767     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32768      /**
32769     * register a Masonry Brick
32770     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32771     */
32772     
32773     register : function(brick)
32774     {
32775         //this.groups[brick.id] = brick;
32776         this.groups.add(brick.id, brick);
32777     },
32778     /**
32779     * fetch a  masonry brick based on the masonry brick ID
32780     * @param {string} the masonry brick to add
32781     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32782     */
32783     
32784     get: function(brick_id) 
32785     {
32786         // if (typeof(this.groups[brick_id]) == 'undefined') {
32787         //     return false;
32788         // }
32789         // return this.groups[brick_id] ;
32790         
32791         if(this.groups.key(brick_id)) {
32792             return this.groups.key(brick_id);
32793         }
32794         
32795         return false;
32796     }
32797     
32798     
32799     
32800 });
32801
32802  /*
32803  * - LGPL
32804  *
32805  * element
32806  * 
32807  */
32808
32809 /**
32810  * @class Roo.bootstrap.Brick
32811  * @extends Roo.bootstrap.Component
32812  * Bootstrap Brick class
32813  * 
32814  * @constructor
32815  * Create a new Brick
32816  * @param {Object} config The config object
32817  */
32818
32819 Roo.bootstrap.Brick = function(config){
32820     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32821     
32822     this.addEvents({
32823         // raw events
32824         /**
32825          * @event click
32826          * When a Brick is click
32827          * @param {Roo.bootstrap.Brick} this
32828          * @param {Roo.EventObject} e
32829          */
32830         "click" : true
32831     });
32832 };
32833
32834 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32835     
32836     /**
32837      * @cfg {String} title
32838      */   
32839     title : '',
32840     /**
32841      * @cfg {String} html
32842      */   
32843     html : '',
32844     /**
32845      * @cfg {String} bgimage
32846      */   
32847     bgimage : '',
32848     /**
32849      * @cfg {String} cls
32850      */   
32851     cls : '',
32852     /**
32853      * @cfg {String} href
32854      */   
32855     href : '',
32856     /**
32857      * @cfg {String} video
32858      */   
32859     video : '',
32860     /**
32861      * @cfg {Boolean} square
32862      */   
32863     square : true,
32864     
32865     getAutoCreate : function()
32866     {
32867         var cls = 'roo-brick';
32868         
32869         if(this.href.length){
32870             cls += ' roo-brick-link';
32871         }
32872         
32873         if(this.bgimage.length){
32874             cls += ' roo-brick-image';
32875         }
32876         
32877         if(!this.html.length && !this.bgimage.length){
32878             cls += ' roo-brick-center-title';
32879         }
32880         
32881         if(!this.html.length && this.bgimage.length){
32882             cls += ' roo-brick-bottom-title';
32883         }
32884         
32885         if(this.cls){
32886             cls += ' ' + this.cls;
32887         }
32888         
32889         var cfg = {
32890             tag: (this.href.length) ? 'a' : 'div',
32891             cls: cls,
32892             cn: [
32893                 {
32894                     tag: 'div',
32895                     cls: 'roo-brick-paragraph',
32896                     cn: []
32897                 }
32898             ]
32899         };
32900         
32901         if(this.href.length){
32902             cfg.href = this.href;
32903         }
32904         
32905         var cn = cfg.cn[0].cn;
32906         
32907         if(this.title.length){
32908             cn.push({
32909                 tag: 'h4',
32910                 cls: 'roo-brick-title',
32911                 html: this.title
32912             });
32913         }
32914         
32915         if(this.html.length){
32916             cn.push({
32917                 tag: 'p',
32918                 cls: 'roo-brick-text',
32919                 html: this.html
32920             });
32921         } else {
32922             cn.cls += ' hide';
32923         }
32924         
32925         if(this.bgimage.length){
32926             cfg.cn.push({
32927                 tag: 'img',
32928                 cls: 'roo-brick-image-view',
32929                 src: this.bgimage
32930             });
32931         }
32932         
32933         return cfg;
32934     },
32935     
32936     initEvents: function() 
32937     {
32938         if(this.title.length || this.html.length){
32939             this.el.on('mouseenter'  ,this.enter, this);
32940             this.el.on('mouseleave', this.leave, this);
32941         }
32942         
32943         Roo.EventManager.onWindowResize(this.resize, this); 
32944         
32945         if(this.bgimage.length){
32946             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32947             this.imageEl.on('load', this.onImageLoad, this);
32948             return;
32949         }
32950         
32951         this.resize();
32952     },
32953     
32954     onImageLoad : function()
32955     {
32956         this.resize();
32957     },
32958     
32959     resize : function()
32960     {
32961         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32962         
32963         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32964         
32965         if(this.bgimage.length){
32966             var image = this.el.select('.roo-brick-image-view', true).first();
32967             
32968             image.setWidth(paragraph.getWidth());
32969             
32970             if(this.square){
32971                 image.setHeight(paragraph.getWidth());
32972             }
32973             
32974             this.el.setHeight(image.getHeight());
32975             paragraph.setHeight(image.getHeight());
32976             
32977         }
32978         
32979     },
32980     
32981     enter: function(e, el)
32982     {
32983         e.preventDefault();
32984         
32985         if(this.bgimage.length){
32986             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32987             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32988         }
32989     },
32990     
32991     leave: function(e, el)
32992     {
32993         e.preventDefault();
32994         
32995         if(this.bgimage.length){
32996             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32997             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32998         }
32999     }
33000     
33001 });
33002
33003  
33004
33005  /*
33006  * - LGPL
33007  *
33008  * Input
33009  * 
33010  */
33011
33012 /**
33013  * @class Roo.bootstrap.NumberField
33014  * @extends Roo.bootstrap.Input
33015  * Bootstrap NumberField class
33016  * 
33017  * 
33018  * 
33019  * 
33020  * @constructor
33021  * Create a new NumberField
33022  * @param {Object} config The config object
33023  */
33024
33025 Roo.bootstrap.NumberField = function(config){
33026     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33027 };
33028
33029 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33030     
33031     /**
33032      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33033      */
33034     allowDecimals : true,
33035     /**
33036      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33037      */
33038     decimalSeparator : ".",
33039     /**
33040      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33041      */
33042     decimalPrecision : 2,
33043     /**
33044      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33045      */
33046     allowNegative : true,
33047     /**
33048      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33049      */
33050     minValue : Number.NEGATIVE_INFINITY,
33051     /**
33052      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33053      */
33054     maxValue : Number.MAX_VALUE,
33055     /**
33056      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33057      */
33058     minText : "The minimum value for this field is {0}",
33059     /**
33060      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33061      */
33062     maxText : "The maximum value for this field is {0}",
33063     /**
33064      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33065      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33066      */
33067     nanText : "{0} is not a valid number",
33068     /**
33069      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33070      */
33071     castInt : true,
33072     /**
33073      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33074      */
33075     thousandsDelimiter : false,
33076     /**
33077      * @cfg {String} valueAlign alignment of value
33078      */
33079     valueAlign : "left",
33080
33081     getAutoCreate : function()
33082     {
33083         var hiddenInput = {
33084             tag: 'input',
33085             type: 'hidden',
33086             id: Roo.id(),
33087             cls: 'hidden-number-input'
33088         };
33089         
33090         if (this.name) {
33091             hiddenInput.name = this.name;
33092         }
33093         
33094         this.name = '';
33095         
33096         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33097         
33098         this.name = hiddenInput.name;
33099         
33100         if(cfg.cn.length > 0) {
33101             cfg.cn.push(hiddenInput);
33102         }
33103         
33104         return cfg;
33105     },
33106
33107     // private
33108     initEvents : function()
33109     {   
33110         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33111         
33112         var allowed = "0123456789";
33113         
33114         if(this.allowDecimals){
33115             allowed += this.decimalSeparator;
33116         }
33117         
33118         if(this.allowNegative){
33119             allowed += "-";
33120         }
33121         
33122         if(this.thousandsDelimiter) {
33123             allowed += ",";
33124         }
33125         
33126         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33127         
33128         var keyPress = function(e){
33129             
33130             var k = e.getKey();
33131             
33132             var c = e.getCharCode();
33133             
33134             if(
33135                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33136                     allowed.indexOf(String.fromCharCode(c)) === -1
33137             ){
33138                 e.stopEvent();
33139                 return;
33140             }
33141             
33142             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33143                 return;
33144             }
33145             
33146             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33147                 e.stopEvent();
33148             }
33149         };
33150         
33151         this.el.on("keypress", keyPress, this);
33152     },
33153     
33154     validateValue : function(value)
33155     {
33156         
33157         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33158             return false;
33159         }
33160         
33161         var num = this.parseValue(value);
33162         
33163         if(isNaN(num)){
33164             this.markInvalid(String.format(this.nanText, value));
33165             return false;
33166         }
33167         
33168         if(num < this.minValue){
33169             this.markInvalid(String.format(this.minText, this.minValue));
33170             return false;
33171         }
33172         
33173         if(num > this.maxValue){
33174             this.markInvalid(String.format(this.maxText, this.maxValue));
33175             return false;
33176         }
33177         
33178         return true;
33179     },
33180
33181     getValue : function()
33182     {
33183         var v = this.hiddenEl().getValue();
33184         
33185         return this.fixPrecision(this.parseValue(v));
33186     },
33187
33188     parseValue : function(value)
33189     {
33190         if(this.thousandsDelimiter) {
33191             value += "";
33192             r = new RegExp(",", "g");
33193             value = value.replace(r, "");
33194         }
33195         
33196         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33197         return isNaN(value) ? '' : value;
33198     },
33199
33200     fixPrecision : function(value)
33201     {
33202         if(this.thousandsDelimiter) {
33203             value += "";
33204             r = new RegExp(",", "g");
33205             value = value.replace(r, "");
33206         }
33207         
33208         var nan = isNaN(value);
33209         
33210         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33211             return nan ? '' : value;
33212         }
33213         return parseFloat(value).toFixed(this.decimalPrecision);
33214     },
33215
33216     setValue : function(v)
33217     {
33218         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33219         
33220         this.value = v;
33221         
33222         Roo.log(v);
33223         
33224         if(this.rendered){
33225             
33226             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33227             
33228             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
33229                 this.thousandsDelimiter || ''
33230             );
33231             
33232             if(this.allowBlank && !v) {
33233                 this.inputEl().dom.value = '';
33234             }
33235             
33236             this.validate();
33237         }
33238     },
33239
33240     decimalPrecisionFcn : function(v)
33241     {
33242         return Math.floor(v);
33243     },
33244
33245     beforeBlur : function()
33246     {
33247         if(!this.castInt){
33248             return;
33249         }
33250         
33251         var v = this.parseValue(this.getRawValue());
33252         
33253         if(v || v == 0){
33254             this.setValue(v);
33255         }
33256     },
33257     
33258     hiddenEl : function()
33259     {
33260         return this.el.select('input.hidden-number-input',true).first();
33261     }
33262     
33263 });
33264
33265  
33266
33267 /*
33268 * Licence: LGPL
33269 */
33270
33271 /**
33272  * @class Roo.bootstrap.DocumentSlider
33273  * @extends Roo.bootstrap.Component
33274  * Bootstrap DocumentSlider class
33275  * 
33276  * @constructor
33277  * Create a new DocumentViewer
33278  * @param {Object} config The config object
33279  */
33280
33281 Roo.bootstrap.DocumentSlider = function(config){
33282     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33283     
33284     this.files = [];
33285     
33286     this.addEvents({
33287         /**
33288          * @event initial
33289          * Fire after initEvent
33290          * @param {Roo.bootstrap.DocumentSlider} this
33291          */
33292         "initial" : true,
33293         /**
33294          * @event update
33295          * Fire after update
33296          * @param {Roo.bootstrap.DocumentSlider} this
33297          */
33298         "update" : true,
33299         /**
33300          * @event click
33301          * Fire after click
33302          * @param {Roo.bootstrap.DocumentSlider} this
33303          */
33304         "click" : true
33305     });
33306 };
33307
33308 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33309     
33310     files : false,
33311     
33312     indicator : 0,
33313     
33314     getAutoCreate : function()
33315     {
33316         var cfg = {
33317             tag : 'div',
33318             cls : 'roo-document-slider',
33319             cn : [
33320                 {
33321                     tag : 'div',
33322                     cls : 'roo-document-slider-header',
33323                     cn : [
33324                         {
33325                             tag : 'div',
33326                             cls : 'roo-document-slider-header-title'
33327                         }
33328                     ]
33329                 },
33330                 {
33331                     tag : 'div',
33332                     cls : 'roo-document-slider-body',
33333                     cn : [
33334                         {
33335                             tag : 'div',
33336                             cls : 'roo-document-slider-prev',
33337                             cn : [
33338                                 {
33339                                     tag : 'i',
33340                                     cls : 'fa fa-chevron-left'
33341                                 }
33342                             ]
33343                         },
33344                         {
33345                             tag : 'div',
33346                             cls : 'roo-document-slider-thumb',
33347                             cn : [
33348                                 {
33349                                     tag : 'img',
33350                                     cls : 'roo-document-slider-image'
33351                                 }
33352                             ]
33353                         },
33354                         {
33355                             tag : 'div',
33356                             cls : 'roo-document-slider-next',
33357                             cn : [
33358                                 {
33359                                     tag : 'i',
33360                                     cls : 'fa fa-chevron-right'
33361                                 }
33362                             ]
33363                         }
33364                     ]
33365                 }
33366             ]
33367         };
33368         
33369         return cfg;
33370     },
33371     
33372     initEvents : function()
33373     {
33374         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33375         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33376         
33377         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33378         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33379         
33380         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33381         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33382         
33383         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33384         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33385         
33386         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33387         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33388         
33389         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33390         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33391         
33392         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33393         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33394         
33395         this.thumbEl.on('click', this.onClick, this);
33396         
33397         this.prevIndicator.on('click', this.prev, this);
33398         
33399         this.nextIndicator.on('click', this.next, this);
33400         
33401     },
33402     
33403     initial : function()
33404     {
33405         if(this.files.length){
33406             this.indicator = 1;
33407             this.update()
33408         }
33409         
33410         this.fireEvent('initial', this);
33411     },
33412     
33413     update : function()
33414     {
33415         this.imageEl.attr('src', this.files[this.indicator - 1]);
33416         
33417         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33418         
33419         this.prevIndicator.show();
33420         
33421         if(this.indicator == 1){
33422             this.prevIndicator.hide();
33423         }
33424         
33425         this.nextIndicator.show();
33426         
33427         if(this.indicator == this.files.length){
33428             this.nextIndicator.hide();
33429         }
33430         
33431         this.thumbEl.scrollTo('top');
33432         
33433         this.fireEvent('update', this);
33434     },
33435     
33436     onClick : function(e)
33437     {
33438         e.preventDefault();
33439         
33440         this.fireEvent('click', this);
33441     },
33442     
33443     prev : function(e)
33444     {
33445         e.preventDefault();
33446         
33447         this.indicator = Math.max(1, this.indicator - 1);
33448         
33449         this.update();
33450     },
33451     
33452     next : function(e)
33453     {
33454         e.preventDefault();
33455         
33456         this.indicator = Math.min(this.files.length, this.indicator + 1);
33457         
33458         this.update();
33459     }
33460 });
33461 /*
33462  * - LGPL
33463  *
33464  * RadioSet
33465  *
33466  *
33467  */
33468
33469 /**
33470  * @class Roo.bootstrap.RadioSet
33471  * @extends Roo.bootstrap.Input
33472  * Bootstrap RadioSet class
33473  * @cfg {String} indicatorpos (left|right) default left
33474  * @cfg {Boolean} inline (true|false) inline the element (default true)
33475  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33476  * @constructor
33477  * Create a new RadioSet
33478  * @param {Object} config The config object
33479  */
33480
33481 Roo.bootstrap.RadioSet = function(config){
33482     
33483     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33484     
33485     this.radioes = [];
33486     
33487     Roo.bootstrap.RadioSet.register(this);
33488     
33489     this.addEvents({
33490         /**
33491         * @event check
33492         * Fires when the element is checked or unchecked.
33493         * @param {Roo.bootstrap.RadioSet} this This radio
33494         * @param {Roo.bootstrap.Radio} item The checked item
33495         */
33496        check : true
33497     });
33498     
33499 };
33500
33501 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33502
33503     radioes : false,
33504     
33505     inline : true,
33506     
33507     weight : '',
33508     
33509     indicatorpos : 'left',
33510     
33511     getAutoCreate : function()
33512     {
33513         var label = {
33514             tag : 'label',
33515             cls : 'roo-radio-set-label',
33516             cn : [
33517                 {
33518                     tag : 'span',
33519                     html : this.fieldLabel
33520                 }
33521             ]
33522         };
33523         
33524         if(this.indicatorpos == 'left'){
33525             label.cn.unshift({
33526                 tag : 'i',
33527                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33528                 tooltip : 'This field is required'
33529             });
33530         } else {
33531             label.cn.push({
33532                 tag : 'i',
33533                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33534                 tooltip : 'This field is required'
33535             });
33536         }
33537         
33538         var items = {
33539             tag : 'div',
33540             cls : 'roo-radio-set-items'
33541         };
33542         
33543         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33544         
33545         if (align === 'left' && this.fieldLabel.length) {
33546             
33547             items = {
33548                 cls : "roo-radio-set-right", 
33549                 cn: [
33550                     items
33551                 ]
33552             };
33553             
33554             if(this.labelWidth > 12){
33555                 label.style = "width: " + this.labelWidth + 'px';
33556             }
33557             
33558             if(this.labelWidth < 13 && this.labelmd == 0){
33559                 this.labelmd = this.labelWidth;
33560             }
33561             
33562             if(this.labellg > 0){
33563                 label.cls += ' col-lg-' + this.labellg;
33564                 items.cls += ' col-lg-' + (12 - this.labellg);
33565             }
33566             
33567             if(this.labelmd > 0){
33568                 label.cls += ' col-md-' + this.labelmd;
33569                 items.cls += ' col-md-' + (12 - this.labelmd);
33570             }
33571             
33572             if(this.labelsm > 0){
33573                 label.cls += ' col-sm-' + this.labelsm;
33574                 items.cls += ' col-sm-' + (12 - this.labelsm);
33575             }
33576             
33577             if(this.labelxs > 0){
33578                 label.cls += ' col-xs-' + this.labelxs;
33579                 items.cls += ' col-xs-' + (12 - this.labelxs);
33580             }
33581         }
33582         
33583         var cfg = {
33584             tag : 'div',
33585             cls : 'roo-radio-set',
33586             cn : [
33587                 {
33588                     tag : 'input',
33589                     cls : 'roo-radio-set-input',
33590                     type : 'hidden',
33591                     name : this.name,
33592                     value : this.value ? this.value :  ''
33593                 },
33594                 label,
33595                 items
33596             ]
33597         };
33598         
33599         if(this.weight.length){
33600             cfg.cls += ' roo-radio-' + this.weight;
33601         }
33602         
33603         if(this.inline) {
33604             cfg.cls += ' roo-radio-set-inline';
33605         }
33606         
33607         var settings=this;
33608         ['xs','sm','md','lg'].map(function(size){
33609             if (settings[size]) {
33610                 cfg.cls += ' col-' + size + '-' + settings[size];
33611             }
33612         });
33613         
33614         return cfg;
33615         
33616     },
33617
33618     initEvents : function()
33619     {
33620         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33621         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33622         
33623         if(!this.fieldLabel.length){
33624             this.labelEl.hide();
33625         }
33626         
33627         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33628         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33629         
33630         this.indicatorEl().addClass('invisible');
33631         
33632         this.originalValue = this.getValue();
33633         
33634     },
33635     
33636     inputEl: function ()
33637     {
33638         return this.el.select('.roo-radio-set-input', true).first();
33639     },
33640     
33641     getChildContainer : function()
33642     {
33643         return this.itemsEl;
33644     },
33645     
33646     register : function(item)
33647     {
33648         this.radioes.push(item);
33649         
33650     },
33651     
33652     validate : function()
33653     {   
33654         var valid = false;
33655         
33656         Roo.each(this.radioes, function(i){
33657             if(!i.checked){
33658                 return;
33659             }
33660             
33661             valid = true;
33662             return false;
33663         });
33664         
33665         if(this.allowBlank) {
33666             return true;
33667         }
33668         
33669         if(this.disabled || valid){
33670             this.markValid();
33671             return true;
33672         }
33673         
33674         this.markInvalid();
33675         return false;
33676         
33677     },
33678     
33679     markValid : function()
33680     {
33681         if(this.labelEl.isVisible(true)){
33682             this.indicatorEl().removeClass('visible');
33683             this.indicatorEl().addClass('invisible');
33684         }
33685         
33686         this.el.removeClass([this.invalidClass, this.validClass]);
33687         this.el.addClass(this.validClass);
33688         
33689         this.fireEvent('valid', this);
33690     },
33691     
33692     markInvalid : function(msg)
33693     {
33694         if(this.allowBlank || this.disabled){
33695             return;
33696         }
33697         
33698         if(this.labelEl.isVisible(true)){
33699             this.indicatorEl().removeClass('invisible');
33700             this.indicatorEl().addClass('visible');
33701         }
33702         
33703         this.el.removeClass([this.invalidClass, this.validClass]);
33704         this.el.addClass(this.invalidClass);
33705         
33706         this.fireEvent('invalid', this, msg);
33707         
33708     },
33709     
33710     setValue : function(v, suppressEvent)
33711     {   
33712         if(this.value === v){
33713             return;
33714         }
33715         
33716         this.value = v;
33717         
33718         if(this.rendered){
33719             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33720         }
33721         
33722         Roo.each(this.radioes, function(i){
33723             i.checked = false;
33724             i.el.removeClass('checked');
33725         });
33726         
33727         Roo.each(this.radioes, function(i){
33728             
33729             if(i.value === v || i.value.toString() === v.toString()){
33730                 i.checked = true;
33731                 i.el.addClass('checked');
33732                 
33733                 if(suppressEvent !== true){
33734                     this.fireEvent('check', this, i);
33735                 }
33736                 
33737                 return false;
33738             }
33739             
33740         }, this);
33741         
33742         this.validate();
33743     },
33744     
33745     clearInvalid : function(){
33746         
33747         if(!this.el || this.preventMark){
33748             return;
33749         }
33750         
33751         this.el.removeClass([this.invalidClass]);
33752         
33753         this.fireEvent('valid', this);
33754     }
33755     
33756 });
33757
33758 Roo.apply(Roo.bootstrap.RadioSet, {
33759     
33760     groups: {},
33761     
33762     register : function(set)
33763     {
33764         this.groups[set.name] = set;
33765     },
33766     
33767     get: function(name) 
33768     {
33769         if (typeof(this.groups[name]) == 'undefined') {
33770             return false;
33771         }
33772         
33773         return this.groups[name] ;
33774     }
33775     
33776 });
33777 /*
33778  * Based on:
33779  * Ext JS Library 1.1.1
33780  * Copyright(c) 2006-2007, Ext JS, LLC.
33781  *
33782  * Originally Released Under LGPL - original licence link has changed is not relivant.
33783  *
33784  * Fork - LGPL
33785  * <script type="text/javascript">
33786  */
33787
33788
33789 /**
33790  * @class Roo.bootstrap.SplitBar
33791  * @extends Roo.util.Observable
33792  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33793  * <br><br>
33794  * Usage:
33795  * <pre><code>
33796 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33797                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33798 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33799 split.minSize = 100;
33800 split.maxSize = 600;
33801 split.animate = true;
33802 split.on('moved', splitterMoved);
33803 </code></pre>
33804  * @constructor
33805  * Create a new SplitBar
33806  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33807  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33808  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33809  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33810                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33811                         position of the SplitBar).
33812  */
33813 Roo.bootstrap.SplitBar = function(cfg){
33814     
33815     /** @private */
33816     
33817     //{
33818     //  dragElement : elm
33819     //  resizingElement: el,
33820         // optional..
33821     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33822     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33823         // existingProxy ???
33824     //}
33825     
33826     this.el = Roo.get(cfg.dragElement, true);
33827     this.el.dom.unselectable = "on";
33828     /** @private */
33829     this.resizingEl = Roo.get(cfg.resizingElement, true);
33830
33831     /**
33832      * @private
33833      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33834      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33835      * @type Number
33836      */
33837     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33838     
33839     /**
33840      * The minimum size of the resizing element. (Defaults to 0)
33841      * @type Number
33842      */
33843     this.minSize = 0;
33844     
33845     /**
33846      * The maximum size of the resizing element. (Defaults to 2000)
33847      * @type Number
33848      */
33849     this.maxSize = 2000;
33850     
33851     /**
33852      * Whether to animate the transition to the new size
33853      * @type Boolean
33854      */
33855     this.animate = false;
33856     
33857     /**
33858      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33859      * @type Boolean
33860      */
33861     this.useShim = false;
33862     
33863     /** @private */
33864     this.shim = null;
33865     
33866     if(!cfg.existingProxy){
33867         /** @private */
33868         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33869     }else{
33870         this.proxy = Roo.get(cfg.existingProxy).dom;
33871     }
33872     /** @private */
33873     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33874     
33875     /** @private */
33876     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33877     
33878     /** @private */
33879     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33880     
33881     /** @private */
33882     this.dragSpecs = {};
33883     
33884     /**
33885      * @private The adapter to use to positon and resize elements
33886      */
33887     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33888     this.adapter.init(this);
33889     
33890     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33891         /** @private */
33892         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33893         this.el.addClass("roo-splitbar-h");
33894     }else{
33895         /** @private */
33896         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33897         this.el.addClass("roo-splitbar-v");
33898     }
33899     
33900     this.addEvents({
33901         /**
33902          * @event resize
33903          * Fires when the splitter is moved (alias for {@link #event-moved})
33904          * @param {Roo.bootstrap.SplitBar} this
33905          * @param {Number} newSize the new width or height
33906          */
33907         "resize" : true,
33908         /**
33909          * @event moved
33910          * Fires when the splitter is moved
33911          * @param {Roo.bootstrap.SplitBar} this
33912          * @param {Number} newSize the new width or height
33913          */
33914         "moved" : true,
33915         /**
33916          * @event beforeresize
33917          * Fires before the splitter is dragged
33918          * @param {Roo.bootstrap.SplitBar} this
33919          */
33920         "beforeresize" : true,
33921
33922         "beforeapply" : true
33923     });
33924
33925     Roo.util.Observable.call(this);
33926 };
33927
33928 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33929     onStartProxyDrag : function(x, y){
33930         this.fireEvent("beforeresize", this);
33931         if(!this.overlay){
33932             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33933             o.unselectable();
33934             o.enableDisplayMode("block");
33935             // all splitbars share the same overlay
33936             Roo.bootstrap.SplitBar.prototype.overlay = o;
33937         }
33938         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33939         this.overlay.show();
33940         Roo.get(this.proxy).setDisplayed("block");
33941         var size = this.adapter.getElementSize(this);
33942         this.activeMinSize = this.getMinimumSize();;
33943         this.activeMaxSize = this.getMaximumSize();;
33944         var c1 = size - this.activeMinSize;
33945         var c2 = Math.max(this.activeMaxSize - size, 0);
33946         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33947             this.dd.resetConstraints();
33948             this.dd.setXConstraint(
33949                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33950                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33951             );
33952             this.dd.setYConstraint(0, 0);
33953         }else{
33954             this.dd.resetConstraints();
33955             this.dd.setXConstraint(0, 0);
33956             this.dd.setYConstraint(
33957                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33958                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33959             );
33960          }
33961         this.dragSpecs.startSize = size;
33962         this.dragSpecs.startPoint = [x, y];
33963         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33964     },
33965     
33966     /** 
33967      * @private Called after the drag operation by the DDProxy
33968      */
33969     onEndProxyDrag : function(e){
33970         Roo.get(this.proxy).setDisplayed(false);
33971         var endPoint = Roo.lib.Event.getXY(e);
33972         if(this.overlay){
33973             this.overlay.hide();
33974         }
33975         var newSize;
33976         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33977             newSize = this.dragSpecs.startSize + 
33978                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33979                     endPoint[0] - this.dragSpecs.startPoint[0] :
33980                     this.dragSpecs.startPoint[0] - endPoint[0]
33981                 );
33982         }else{
33983             newSize = this.dragSpecs.startSize + 
33984                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33985                     endPoint[1] - this.dragSpecs.startPoint[1] :
33986                     this.dragSpecs.startPoint[1] - endPoint[1]
33987                 );
33988         }
33989         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33990         if(newSize != this.dragSpecs.startSize){
33991             if(this.fireEvent('beforeapply', this, newSize) !== false){
33992                 this.adapter.setElementSize(this, newSize);
33993                 this.fireEvent("moved", this, newSize);
33994                 this.fireEvent("resize", this, newSize);
33995             }
33996         }
33997     },
33998     
33999     /**
34000      * Get the adapter this SplitBar uses
34001      * @return The adapter object
34002      */
34003     getAdapter : function(){
34004         return this.adapter;
34005     },
34006     
34007     /**
34008      * Set the adapter this SplitBar uses
34009      * @param {Object} adapter A SplitBar adapter object
34010      */
34011     setAdapter : function(adapter){
34012         this.adapter = adapter;
34013         this.adapter.init(this);
34014     },
34015     
34016     /**
34017      * Gets the minimum size for the resizing element
34018      * @return {Number} The minimum size
34019      */
34020     getMinimumSize : function(){
34021         return this.minSize;
34022     },
34023     
34024     /**
34025      * Sets the minimum size for the resizing element
34026      * @param {Number} minSize The minimum size
34027      */
34028     setMinimumSize : function(minSize){
34029         this.minSize = minSize;
34030     },
34031     
34032     /**
34033      * Gets the maximum size for the resizing element
34034      * @return {Number} The maximum size
34035      */
34036     getMaximumSize : function(){
34037         return this.maxSize;
34038     },
34039     
34040     /**
34041      * Sets the maximum size for the resizing element
34042      * @param {Number} maxSize The maximum size
34043      */
34044     setMaximumSize : function(maxSize){
34045         this.maxSize = maxSize;
34046     },
34047     
34048     /**
34049      * Sets the initialize size for the resizing element
34050      * @param {Number} size The initial size
34051      */
34052     setCurrentSize : function(size){
34053         var oldAnimate = this.animate;
34054         this.animate = false;
34055         this.adapter.setElementSize(this, size);
34056         this.animate = oldAnimate;
34057     },
34058     
34059     /**
34060      * Destroy this splitbar. 
34061      * @param {Boolean} removeEl True to remove the element
34062      */
34063     destroy : function(removeEl){
34064         if(this.shim){
34065             this.shim.remove();
34066         }
34067         this.dd.unreg();
34068         this.proxy.parentNode.removeChild(this.proxy);
34069         if(removeEl){
34070             this.el.remove();
34071         }
34072     }
34073 });
34074
34075 /**
34076  * @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.
34077  */
34078 Roo.bootstrap.SplitBar.createProxy = function(dir){
34079     var proxy = new Roo.Element(document.createElement("div"));
34080     proxy.unselectable();
34081     var cls = 'roo-splitbar-proxy';
34082     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34083     document.body.appendChild(proxy.dom);
34084     return proxy.dom;
34085 };
34086
34087 /** 
34088  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34089  * Default Adapter. It assumes the splitter and resizing element are not positioned
34090  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34091  */
34092 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34093 };
34094
34095 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34096     // do nothing for now
34097     init : function(s){
34098     
34099     },
34100     /**
34101      * Called before drag operations to get the current size of the resizing element. 
34102      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34103      */
34104      getElementSize : function(s){
34105         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34106             return s.resizingEl.getWidth();
34107         }else{
34108             return s.resizingEl.getHeight();
34109         }
34110     },
34111     
34112     /**
34113      * Called after drag operations to set the size of the resizing element.
34114      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34115      * @param {Number} newSize The new size to set
34116      * @param {Function} onComplete A function to be invoked when resizing is complete
34117      */
34118     setElementSize : function(s, newSize, onComplete){
34119         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34120             if(!s.animate){
34121                 s.resizingEl.setWidth(newSize);
34122                 if(onComplete){
34123                     onComplete(s, newSize);
34124                 }
34125             }else{
34126                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34127             }
34128         }else{
34129             
34130             if(!s.animate){
34131                 s.resizingEl.setHeight(newSize);
34132                 if(onComplete){
34133                     onComplete(s, newSize);
34134                 }
34135             }else{
34136                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34137             }
34138         }
34139     }
34140 };
34141
34142 /** 
34143  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34144  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34145  * Adapter that  moves the splitter element to align with the resized sizing element. 
34146  * Used with an absolute positioned SplitBar.
34147  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34148  * document.body, make sure you assign an id to the body element.
34149  */
34150 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34151     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34152     this.container = Roo.get(container);
34153 };
34154
34155 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34156     init : function(s){
34157         this.basic.init(s);
34158     },
34159     
34160     getElementSize : function(s){
34161         return this.basic.getElementSize(s);
34162     },
34163     
34164     setElementSize : function(s, newSize, onComplete){
34165         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34166     },
34167     
34168     moveSplitter : function(s){
34169         var yes = Roo.bootstrap.SplitBar;
34170         switch(s.placement){
34171             case yes.LEFT:
34172                 s.el.setX(s.resizingEl.getRight());
34173                 break;
34174             case yes.RIGHT:
34175                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34176                 break;
34177             case yes.TOP:
34178                 s.el.setY(s.resizingEl.getBottom());
34179                 break;
34180             case yes.BOTTOM:
34181                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34182                 break;
34183         }
34184     }
34185 };
34186
34187 /**
34188  * Orientation constant - Create a vertical SplitBar
34189  * @static
34190  * @type Number
34191  */
34192 Roo.bootstrap.SplitBar.VERTICAL = 1;
34193
34194 /**
34195  * Orientation constant - Create a horizontal SplitBar
34196  * @static
34197  * @type Number
34198  */
34199 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34200
34201 /**
34202  * Placement constant - The resizing element is to the left of the splitter element
34203  * @static
34204  * @type Number
34205  */
34206 Roo.bootstrap.SplitBar.LEFT = 1;
34207
34208 /**
34209  * Placement constant - The resizing element is to the right of the splitter element
34210  * @static
34211  * @type Number
34212  */
34213 Roo.bootstrap.SplitBar.RIGHT = 2;
34214
34215 /**
34216  * Placement constant - The resizing element is positioned above the splitter element
34217  * @static
34218  * @type Number
34219  */
34220 Roo.bootstrap.SplitBar.TOP = 3;
34221
34222 /**
34223  * Placement constant - The resizing element is positioned under splitter element
34224  * @static
34225  * @type Number
34226  */
34227 Roo.bootstrap.SplitBar.BOTTOM = 4;
34228 Roo.namespace("Roo.bootstrap.layout");/*
34229  * Based on:
34230  * Ext JS Library 1.1.1
34231  * Copyright(c) 2006-2007, Ext JS, LLC.
34232  *
34233  * Originally Released Under LGPL - original licence link has changed is not relivant.
34234  *
34235  * Fork - LGPL
34236  * <script type="text/javascript">
34237  */
34238
34239 /**
34240  * @class Roo.bootstrap.layout.Manager
34241  * @extends Roo.bootstrap.Component
34242  * Base class for layout managers.
34243  */
34244 Roo.bootstrap.layout.Manager = function(config)
34245 {
34246     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34247
34248
34249
34250
34251
34252     /** false to disable window resize monitoring @type Boolean */
34253     this.monitorWindowResize = true;
34254     this.regions = {};
34255     this.addEvents({
34256         /**
34257          * @event layout
34258          * Fires when a layout is performed.
34259          * @param {Roo.LayoutManager} this
34260          */
34261         "layout" : true,
34262         /**
34263          * @event regionresized
34264          * Fires when the user resizes a region.
34265          * @param {Roo.LayoutRegion} region The resized region
34266          * @param {Number} newSize The new size (width for east/west, height for north/south)
34267          */
34268         "regionresized" : true,
34269         /**
34270          * @event regioncollapsed
34271          * Fires when a region is collapsed.
34272          * @param {Roo.LayoutRegion} region The collapsed region
34273          */
34274         "regioncollapsed" : true,
34275         /**
34276          * @event regionexpanded
34277          * Fires when a region is expanded.
34278          * @param {Roo.LayoutRegion} region The expanded region
34279          */
34280         "regionexpanded" : true
34281     });
34282     this.updating = false;
34283
34284     if (config.el) {
34285         this.el = Roo.get(config.el);
34286         this.initEvents();
34287     }
34288
34289 };
34290
34291 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34292
34293
34294     regions : null,
34295
34296     monitorWindowResize : true,
34297
34298
34299     updating : false,
34300
34301
34302     onRender : function(ct, position)
34303     {
34304         if(!this.el){
34305             this.el = Roo.get(ct);
34306             this.initEvents();
34307         }
34308         //this.fireEvent('render',this);
34309     },
34310
34311
34312     initEvents: function()
34313     {
34314
34315
34316         // ie scrollbar fix
34317         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34318             document.body.scroll = "no";
34319         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34320             this.el.position('relative');
34321         }
34322         this.id = this.el.id;
34323         this.el.addClass("roo-layout-container");
34324         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34325         if(this.el.dom != document.body ) {
34326             this.el.on('resize', this.layout,this);
34327             this.el.on('show', this.layout,this);
34328         }
34329
34330     },
34331
34332     /**
34333      * Returns true if this layout is currently being updated
34334      * @return {Boolean}
34335      */
34336     isUpdating : function(){
34337         return this.updating;
34338     },
34339
34340     /**
34341      * Suspend the LayoutManager from doing auto-layouts while
34342      * making multiple add or remove calls
34343      */
34344     beginUpdate : function(){
34345         this.updating = true;
34346     },
34347
34348     /**
34349      * Restore auto-layouts and optionally disable the manager from performing a layout
34350      * @param {Boolean} noLayout true to disable a layout update
34351      */
34352     endUpdate : function(noLayout){
34353         this.updating = false;
34354         if(!noLayout){
34355             this.layout();
34356         }
34357     },
34358
34359     layout: function(){
34360         // abstract...
34361     },
34362
34363     onRegionResized : function(region, newSize){
34364         this.fireEvent("regionresized", region, newSize);
34365         this.layout();
34366     },
34367
34368     onRegionCollapsed : function(region){
34369         this.fireEvent("regioncollapsed", region);
34370     },
34371
34372     onRegionExpanded : function(region){
34373         this.fireEvent("regionexpanded", region);
34374     },
34375
34376     /**
34377      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34378      * performs box-model adjustments.
34379      * @return {Object} The size as an object {width: (the width), height: (the height)}
34380      */
34381     getViewSize : function()
34382     {
34383         var size;
34384         if(this.el.dom != document.body){
34385             size = this.el.getSize();
34386         }else{
34387             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34388         }
34389         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34390         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34391         return size;
34392     },
34393
34394     /**
34395      * Returns the Element this layout is bound to.
34396      * @return {Roo.Element}
34397      */
34398     getEl : function(){
34399         return this.el;
34400     },
34401
34402     /**
34403      * Returns the specified region.
34404      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34405      * @return {Roo.LayoutRegion}
34406      */
34407     getRegion : function(target){
34408         return this.regions[target.toLowerCase()];
34409     },
34410
34411     onWindowResize : function(){
34412         if(this.monitorWindowResize){
34413             this.layout();
34414         }
34415     }
34416 });
34417 /*
34418  * Based on:
34419  * Ext JS Library 1.1.1
34420  * Copyright(c) 2006-2007, Ext JS, LLC.
34421  *
34422  * Originally Released Under LGPL - original licence link has changed is not relivant.
34423  *
34424  * Fork - LGPL
34425  * <script type="text/javascript">
34426  */
34427 /**
34428  * @class Roo.bootstrap.layout.Border
34429  * @extends Roo.bootstrap.layout.Manager
34430  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34431  * please see: examples/bootstrap/nested.html<br><br>
34432  
34433 <b>The container the layout is rendered into can be either the body element or any other element.
34434 If it is not the body element, the container needs to either be an absolute positioned element,
34435 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34436 the container size if it is not the body element.</b>
34437
34438 * @constructor
34439 * Create a new Border
34440 * @param {Object} config Configuration options
34441  */
34442 Roo.bootstrap.layout.Border = function(config){
34443     config = config || {};
34444     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34445     
34446     
34447     
34448     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34449         if(config[region]){
34450             config[region].region = region;
34451             this.addRegion(config[region]);
34452         }
34453     },this);
34454     
34455 };
34456
34457 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34458
34459 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34460     /**
34461      * Creates and adds a new region if it doesn't already exist.
34462      * @param {String} target The target region key (north, south, east, west or center).
34463      * @param {Object} config The regions config object
34464      * @return {BorderLayoutRegion} The new region
34465      */
34466     addRegion : function(config)
34467     {
34468         if(!this.regions[config.region]){
34469             var r = this.factory(config);
34470             this.bindRegion(r);
34471         }
34472         return this.regions[config.region];
34473     },
34474
34475     // private (kinda)
34476     bindRegion : function(r){
34477         this.regions[r.config.region] = r;
34478         
34479         r.on("visibilitychange",    this.layout, this);
34480         r.on("paneladded",          this.layout, this);
34481         r.on("panelremoved",        this.layout, this);
34482         r.on("invalidated",         this.layout, this);
34483         r.on("resized",             this.onRegionResized, this);
34484         r.on("collapsed",           this.onRegionCollapsed, this);
34485         r.on("expanded",            this.onRegionExpanded, this);
34486     },
34487
34488     /**
34489      * Performs a layout update.
34490      */
34491     layout : function()
34492     {
34493         if(this.updating) {
34494             return;
34495         }
34496         
34497         // render all the rebions if they have not been done alreayd?
34498         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34499             if(this.regions[region] && !this.regions[region].bodyEl){
34500                 this.regions[region].onRender(this.el)
34501             }
34502         },this);
34503         
34504         var size = this.getViewSize();
34505         var w = size.width;
34506         var h = size.height;
34507         var centerW = w;
34508         var centerH = h;
34509         var centerY = 0;
34510         var centerX = 0;
34511         //var x = 0, y = 0;
34512
34513         var rs = this.regions;
34514         var north = rs["north"];
34515         var south = rs["south"]; 
34516         var west = rs["west"];
34517         var east = rs["east"];
34518         var center = rs["center"];
34519         //if(this.hideOnLayout){ // not supported anymore
34520             //c.el.setStyle("display", "none");
34521         //}
34522         if(north && north.isVisible()){
34523             var b = north.getBox();
34524             var m = north.getMargins();
34525             b.width = w - (m.left+m.right);
34526             b.x = m.left;
34527             b.y = m.top;
34528             centerY = b.height + b.y + m.bottom;
34529             centerH -= centerY;
34530             north.updateBox(this.safeBox(b));
34531         }
34532         if(south && south.isVisible()){
34533             var b = south.getBox();
34534             var m = south.getMargins();
34535             b.width = w - (m.left+m.right);
34536             b.x = m.left;
34537             var totalHeight = (b.height + m.top + m.bottom);
34538             b.y = h - totalHeight + m.top;
34539             centerH -= totalHeight;
34540             south.updateBox(this.safeBox(b));
34541         }
34542         if(west && west.isVisible()){
34543             var b = west.getBox();
34544             var m = west.getMargins();
34545             b.height = centerH - (m.top+m.bottom);
34546             b.x = m.left;
34547             b.y = centerY + m.top;
34548             var totalWidth = (b.width + m.left + m.right);
34549             centerX += totalWidth;
34550             centerW -= totalWidth;
34551             west.updateBox(this.safeBox(b));
34552         }
34553         if(east && east.isVisible()){
34554             var b = east.getBox();
34555             var m = east.getMargins();
34556             b.height = centerH - (m.top+m.bottom);
34557             var totalWidth = (b.width + m.left + m.right);
34558             b.x = w - totalWidth + m.left;
34559             b.y = centerY + m.top;
34560             centerW -= totalWidth;
34561             east.updateBox(this.safeBox(b));
34562         }
34563         if(center){
34564             var m = center.getMargins();
34565             var centerBox = {
34566                 x: centerX + m.left,
34567                 y: centerY + m.top,
34568                 width: centerW - (m.left+m.right),
34569                 height: centerH - (m.top+m.bottom)
34570             };
34571             //if(this.hideOnLayout){
34572                 //center.el.setStyle("display", "block");
34573             //}
34574             center.updateBox(this.safeBox(centerBox));
34575         }
34576         this.el.repaint();
34577         this.fireEvent("layout", this);
34578     },
34579
34580     // private
34581     safeBox : function(box){
34582         box.width = Math.max(0, box.width);
34583         box.height = Math.max(0, box.height);
34584         return box;
34585     },
34586
34587     /**
34588      * Adds a ContentPanel (or subclass) to this layout.
34589      * @param {String} target The target region key (north, south, east, west or center).
34590      * @param {Roo.ContentPanel} panel The panel to add
34591      * @return {Roo.ContentPanel} The added panel
34592      */
34593     add : function(target, panel){
34594          
34595         target = target.toLowerCase();
34596         return this.regions[target].add(panel);
34597     },
34598
34599     /**
34600      * Remove a ContentPanel (or subclass) to this layout.
34601      * @param {String} target The target region key (north, south, east, west or center).
34602      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34603      * @return {Roo.ContentPanel} The removed panel
34604      */
34605     remove : function(target, panel){
34606         target = target.toLowerCase();
34607         return this.regions[target].remove(panel);
34608     },
34609
34610     /**
34611      * Searches all regions for a panel with the specified id
34612      * @param {String} panelId
34613      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34614      */
34615     findPanel : function(panelId){
34616         var rs = this.regions;
34617         for(var target in rs){
34618             if(typeof rs[target] != "function"){
34619                 var p = rs[target].getPanel(panelId);
34620                 if(p){
34621                     return p;
34622                 }
34623             }
34624         }
34625         return null;
34626     },
34627
34628     /**
34629      * Searches all regions for a panel with the specified id and activates (shows) it.
34630      * @param {String/ContentPanel} panelId The panels id or the panel itself
34631      * @return {Roo.ContentPanel} The shown panel or null
34632      */
34633     showPanel : function(panelId) {
34634       var rs = this.regions;
34635       for(var target in rs){
34636          var r = rs[target];
34637          if(typeof r != "function"){
34638             if(r.hasPanel(panelId)){
34639                return r.showPanel(panelId);
34640             }
34641          }
34642       }
34643       return null;
34644    },
34645
34646    /**
34647      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34648      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34649      */
34650    /*
34651     restoreState : function(provider){
34652         if(!provider){
34653             provider = Roo.state.Manager;
34654         }
34655         var sm = new Roo.LayoutStateManager();
34656         sm.init(this, provider);
34657     },
34658 */
34659  
34660  
34661     /**
34662      * Adds a xtype elements to the layout.
34663      * <pre><code>
34664
34665 layout.addxtype({
34666        xtype : 'ContentPanel',
34667        region: 'west',
34668        items: [ .... ]
34669    }
34670 );
34671
34672 layout.addxtype({
34673         xtype : 'NestedLayoutPanel',
34674         region: 'west',
34675         layout: {
34676            center: { },
34677            west: { }   
34678         },
34679         items : [ ... list of content panels or nested layout panels.. ]
34680    }
34681 );
34682 </code></pre>
34683      * @param {Object} cfg Xtype definition of item to add.
34684      */
34685     addxtype : function(cfg)
34686     {
34687         // basically accepts a pannel...
34688         // can accept a layout region..!?!?
34689         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34690         
34691         
34692         // theory?  children can only be panels??
34693         
34694         //if (!cfg.xtype.match(/Panel$/)) {
34695         //    return false;
34696         //}
34697         var ret = false;
34698         
34699         if (typeof(cfg.region) == 'undefined') {
34700             Roo.log("Failed to add Panel, region was not set");
34701             Roo.log(cfg);
34702             return false;
34703         }
34704         var region = cfg.region;
34705         delete cfg.region;
34706         
34707           
34708         var xitems = [];
34709         if (cfg.items) {
34710             xitems = cfg.items;
34711             delete cfg.items;
34712         }
34713         var nb = false;
34714         
34715         switch(cfg.xtype) 
34716         {
34717             case 'Content':  // ContentPanel (el, cfg)
34718             case 'Scroll':  // ContentPanel (el, cfg)
34719             case 'View': 
34720                 cfg.autoCreate = true;
34721                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34722                 //} else {
34723                 //    var el = this.el.createChild();
34724                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34725                 //}
34726                 
34727                 this.add(region, ret);
34728                 break;
34729             
34730             /*
34731             case 'TreePanel': // our new panel!
34732                 cfg.el = this.el.createChild();
34733                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34734                 this.add(region, ret);
34735                 break;
34736             */
34737             
34738             case 'Nest': 
34739                 // create a new Layout (which is  a Border Layout...
34740                 
34741                 var clayout = cfg.layout;
34742                 clayout.el  = this.el.createChild();
34743                 clayout.items   = clayout.items  || [];
34744                 
34745                 delete cfg.layout;
34746                 
34747                 // replace this exitems with the clayout ones..
34748                 xitems = clayout.items;
34749                  
34750                 // force background off if it's in center...
34751                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34752                     cfg.background = false;
34753                 }
34754                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34755                 
34756                 
34757                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34758                 //console.log('adding nested layout panel '  + cfg.toSource());
34759                 this.add(region, ret);
34760                 nb = {}; /// find first...
34761                 break;
34762             
34763             case 'Grid':
34764                 
34765                 // needs grid and region
34766                 
34767                 //var el = this.getRegion(region).el.createChild();
34768                 /*
34769                  *var el = this.el.createChild();
34770                 // create the grid first...
34771                 cfg.grid.container = el;
34772                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34773                 */
34774                 
34775                 if (region == 'center' && this.active ) {
34776                     cfg.background = false;
34777                 }
34778                 
34779                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34780                 
34781                 this.add(region, ret);
34782                 /*
34783                 if (cfg.background) {
34784                     // render grid on panel activation (if panel background)
34785                     ret.on('activate', function(gp) {
34786                         if (!gp.grid.rendered) {
34787                     //        gp.grid.render(el);
34788                         }
34789                     });
34790                 } else {
34791                   //  cfg.grid.render(el);
34792                 }
34793                 */
34794                 break;
34795            
34796            
34797             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34798                 // it was the old xcomponent building that caused this before.
34799                 // espeically if border is the top element in the tree.
34800                 ret = this;
34801                 break; 
34802                 
34803                     
34804                 
34805                 
34806                 
34807             default:
34808                 /*
34809                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34810                     
34811                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34812                     this.add(region, ret);
34813                 } else {
34814                 */
34815                     Roo.log(cfg);
34816                     throw "Can not add '" + cfg.xtype + "' to Border";
34817                     return null;
34818              
34819                                 
34820              
34821         }
34822         this.beginUpdate();
34823         // add children..
34824         var region = '';
34825         var abn = {};
34826         Roo.each(xitems, function(i)  {
34827             region = nb && i.region ? i.region : false;
34828             
34829             var add = ret.addxtype(i);
34830            
34831             if (region) {
34832                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34833                 if (!i.background) {
34834                     abn[region] = nb[region] ;
34835                 }
34836             }
34837             
34838         });
34839         this.endUpdate();
34840
34841         // make the last non-background panel active..
34842         //if (nb) { Roo.log(abn); }
34843         if (nb) {
34844             
34845             for(var r in abn) {
34846                 region = this.getRegion(r);
34847                 if (region) {
34848                     // tried using nb[r], but it does not work..
34849                      
34850                     region.showPanel(abn[r]);
34851                    
34852                 }
34853             }
34854         }
34855         return ret;
34856         
34857     },
34858     
34859     
34860 // private
34861     factory : function(cfg)
34862     {
34863         
34864         var validRegions = Roo.bootstrap.layout.Border.regions;
34865
34866         var target = cfg.region;
34867         cfg.mgr = this;
34868         
34869         var r = Roo.bootstrap.layout;
34870         Roo.log(target);
34871         switch(target){
34872             case "north":
34873                 return new r.North(cfg);
34874             case "south":
34875                 return new r.South(cfg);
34876             case "east":
34877                 return new r.East(cfg);
34878             case "west":
34879                 return new r.West(cfg);
34880             case "center":
34881                 return new r.Center(cfg);
34882         }
34883         throw 'Layout region "'+target+'" not supported.';
34884     }
34885     
34886     
34887 });
34888  /*
34889  * Based on:
34890  * Ext JS Library 1.1.1
34891  * Copyright(c) 2006-2007, Ext JS, LLC.
34892  *
34893  * Originally Released Under LGPL - original licence link has changed is not relivant.
34894  *
34895  * Fork - LGPL
34896  * <script type="text/javascript">
34897  */
34898  
34899 /**
34900  * @class Roo.bootstrap.layout.Basic
34901  * @extends Roo.util.Observable
34902  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34903  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34904  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34905  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34906  * @cfg {string}   region  the region that it inhabits..
34907  * @cfg {bool}   skipConfig skip config?
34908  * 
34909
34910  */
34911 Roo.bootstrap.layout.Basic = function(config){
34912     
34913     this.mgr = config.mgr;
34914     
34915     this.position = config.region;
34916     
34917     var skipConfig = config.skipConfig;
34918     
34919     this.events = {
34920         /**
34921          * @scope Roo.BasicLayoutRegion
34922          */
34923         
34924         /**
34925          * @event beforeremove
34926          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34927          * @param {Roo.LayoutRegion} this
34928          * @param {Roo.ContentPanel} panel The panel
34929          * @param {Object} e The cancel event object
34930          */
34931         "beforeremove" : true,
34932         /**
34933          * @event invalidated
34934          * Fires when the layout for this region is changed.
34935          * @param {Roo.LayoutRegion} this
34936          */
34937         "invalidated" : true,
34938         /**
34939          * @event visibilitychange
34940          * Fires when this region is shown or hidden 
34941          * @param {Roo.LayoutRegion} this
34942          * @param {Boolean} visibility true or false
34943          */
34944         "visibilitychange" : true,
34945         /**
34946          * @event paneladded
34947          * Fires when a panel is added. 
34948          * @param {Roo.LayoutRegion} this
34949          * @param {Roo.ContentPanel} panel The panel
34950          */
34951         "paneladded" : true,
34952         /**
34953          * @event panelremoved
34954          * Fires when a panel is removed. 
34955          * @param {Roo.LayoutRegion} this
34956          * @param {Roo.ContentPanel} panel The panel
34957          */
34958         "panelremoved" : true,
34959         /**
34960          * @event beforecollapse
34961          * Fires when this region before collapse.
34962          * @param {Roo.LayoutRegion} this
34963          */
34964         "beforecollapse" : true,
34965         /**
34966          * @event collapsed
34967          * Fires when this region is collapsed.
34968          * @param {Roo.LayoutRegion} this
34969          */
34970         "collapsed" : true,
34971         /**
34972          * @event expanded
34973          * Fires when this region is expanded.
34974          * @param {Roo.LayoutRegion} this
34975          */
34976         "expanded" : true,
34977         /**
34978          * @event slideshow
34979          * Fires when this region is slid into view.
34980          * @param {Roo.LayoutRegion} this
34981          */
34982         "slideshow" : true,
34983         /**
34984          * @event slidehide
34985          * Fires when this region slides out of view. 
34986          * @param {Roo.LayoutRegion} this
34987          */
34988         "slidehide" : true,
34989         /**
34990          * @event panelactivated
34991          * Fires when a panel is activated. 
34992          * @param {Roo.LayoutRegion} this
34993          * @param {Roo.ContentPanel} panel The activated panel
34994          */
34995         "panelactivated" : true,
34996         /**
34997          * @event resized
34998          * Fires when the user resizes this region. 
34999          * @param {Roo.LayoutRegion} this
35000          * @param {Number} newSize The new size (width for east/west, height for north/south)
35001          */
35002         "resized" : true
35003     };
35004     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35005     this.panels = new Roo.util.MixedCollection();
35006     this.panels.getKey = this.getPanelId.createDelegate(this);
35007     this.box = null;
35008     this.activePanel = null;
35009     // ensure listeners are added...
35010     
35011     if (config.listeners || config.events) {
35012         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35013             listeners : config.listeners || {},
35014             events : config.events || {}
35015         });
35016     }
35017     
35018     if(skipConfig !== true){
35019         this.applyConfig(config);
35020     }
35021 };
35022
35023 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35024 {
35025     getPanelId : function(p){
35026         return p.getId();
35027     },
35028     
35029     applyConfig : function(config){
35030         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35031         this.config = config;
35032         
35033     },
35034     
35035     /**
35036      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35037      * the width, for horizontal (north, south) the height.
35038      * @param {Number} newSize The new width or height
35039      */
35040     resizeTo : function(newSize){
35041         var el = this.el ? this.el :
35042                  (this.activePanel ? this.activePanel.getEl() : null);
35043         if(el){
35044             switch(this.position){
35045                 case "east":
35046                 case "west":
35047                     el.setWidth(newSize);
35048                     this.fireEvent("resized", this, newSize);
35049                 break;
35050                 case "north":
35051                 case "south":
35052                     el.setHeight(newSize);
35053                     this.fireEvent("resized", this, newSize);
35054                 break;                
35055             }
35056         }
35057     },
35058     
35059     getBox : function(){
35060         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35061     },
35062     
35063     getMargins : function(){
35064         return this.margins;
35065     },
35066     
35067     updateBox : function(box){
35068         this.box = box;
35069         var el = this.activePanel.getEl();
35070         el.dom.style.left = box.x + "px";
35071         el.dom.style.top = box.y + "px";
35072         this.activePanel.setSize(box.width, box.height);
35073     },
35074     
35075     /**
35076      * Returns the container element for this region.
35077      * @return {Roo.Element}
35078      */
35079     getEl : function(){
35080         return this.activePanel;
35081     },
35082     
35083     /**
35084      * Returns true if this region is currently visible.
35085      * @return {Boolean}
35086      */
35087     isVisible : function(){
35088         return this.activePanel ? true : false;
35089     },
35090     
35091     setActivePanel : function(panel){
35092         panel = this.getPanel(panel);
35093         if(this.activePanel && this.activePanel != panel){
35094             this.activePanel.setActiveState(false);
35095             this.activePanel.getEl().setLeftTop(-10000,-10000);
35096         }
35097         this.activePanel = panel;
35098         panel.setActiveState(true);
35099         if(this.box){
35100             panel.setSize(this.box.width, this.box.height);
35101         }
35102         this.fireEvent("panelactivated", this, panel);
35103         this.fireEvent("invalidated");
35104     },
35105     
35106     /**
35107      * Show the specified panel.
35108      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35109      * @return {Roo.ContentPanel} The shown panel or null
35110      */
35111     showPanel : function(panel){
35112         panel = this.getPanel(panel);
35113         if(panel){
35114             this.setActivePanel(panel);
35115         }
35116         return panel;
35117     },
35118     
35119     /**
35120      * Get the active panel for this region.
35121      * @return {Roo.ContentPanel} The active panel or null
35122      */
35123     getActivePanel : function(){
35124         return this.activePanel;
35125     },
35126     
35127     /**
35128      * Add the passed ContentPanel(s)
35129      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35130      * @return {Roo.ContentPanel} The panel added (if only one was added)
35131      */
35132     add : function(panel){
35133         if(arguments.length > 1){
35134             for(var i = 0, len = arguments.length; i < len; i++) {
35135                 this.add(arguments[i]);
35136             }
35137             return null;
35138         }
35139         if(this.hasPanel(panel)){
35140             this.showPanel(panel);
35141             return panel;
35142         }
35143         var el = panel.getEl();
35144         if(el.dom.parentNode != this.mgr.el.dom){
35145             this.mgr.el.dom.appendChild(el.dom);
35146         }
35147         if(panel.setRegion){
35148             panel.setRegion(this);
35149         }
35150         this.panels.add(panel);
35151         el.setStyle("position", "absolute");
35152         if(!panel.background){
35153             this.setActivePanel(panel);
35154             if(this.config.initialSize && this.panels.getCount()==1){
35155                 this.resizeTo(this.config.initialSize);
35156             }
35157         }
35158         this.fireEvent("paneladded", this, panel);
35159         return panel;
35160     },
35161     
35162     /**
35163      * Returns true if the panel is in this region.
35164      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35165      * @return {Boolean}
35166      */
35167     hasPanel : function(panel){
35168         if(typeof panel == "object"){ // must be panel obj
35169             panel = panel.getId();
35170         }
35171         return this.getPanel(panel) ? true : false;
35172     },
35173     
35174     /**
35175      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35176      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35177      * @param {Boolean} preservePanel Overrides the config preservePanel option
35178      * @return {Roo.ContentPanel} The panel that was removed
35179      */
35180     remove : function(panel, preservePanel){
35181         panel = this.getPanel(panel);
35182         if(!panel){
35183             return null;
35184         }
35185         var e = {};
35186         this.fireEvent("beforeremove", this, panel, e);
35187         if(e.cancel === true){
35188             return null;
35189         }
35190         var panelId = panel.getId();
35191         this.panels.removeKey(panelId);
35192         return panel;
35193     },
35194     
35195     /**
35196      * Returns the panel specified or null if it's not in this region.
35197      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35198      * @return {Roo.ContentPanel}
35199      */
35200     getPanel : function(id){
35201         if(typeof id == "object"){ // must be panel obj
35202             return id;
35203         }
35204         return this.panels.get(id);
35205     },
35206     
35207     /**
35208      * Returns this regions position (north/south/east/west/center).
35209      * @return {String} 
35210      */
35211     getPosition: function(){
35212         return this.position;    
35213     }
35214 });/*
35215  * Based on:
35216  * Ext JS Library 1.1.1
35217  * Copyright(c) 2006-2007, Ext JS, LLC.
35218  *
35219  * Originally Released Under LGPL - original licence link has changed is not relivant.
35220  *
35221  * Fork - LGPL
35222  * <script type="text/javascript">
35223  */
35224  
35225 /**
35226  * @class Roo.bootstrap.layout.Region
35227  * @extends Roo.bootstrap.layout.Basic
35228  * This class represents a region in a layout manager.
35229  
35230  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35231  * @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})
35232  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35233  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35234  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35235  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35236  * @cfg {String}    title           The title for the region (overrides panel titles)
35237  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35238  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35239  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35240  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35241  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35242  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35243  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35244  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35245  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35246  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35247
35248  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35249  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35250  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35251  * @cfg {Number}    width           For East/West panels
35252  * @cfg {Number}    height          For North/South panels
35253  * @cfg {Boolean}   split           To show the splitter
35254  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35255  * 
35256  * @cfg {string}   cls             Extra CSS classes to add to region
35257  * 
35258  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35259  * @cfg {string}   region  the region that it inhabits..
35260  *
35261
35262  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35263  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35264
35265  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35266  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35267  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35268  */
35269 Roo.bootstrap.layout.Region = function(config)
35270 {
35271     this.applyConfig(config);
35272
35273     var mgr = config.mgr;
35274     var pos = config.region;
35275     config.skipConfig = true;
35276     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35277     
35278     if (mgr.el) {
35279         this.onRender(mgr.el);   
35280     }
35281      
35282     this.visible = true;
35283     this.collapsed = false;
35284     this.unrendered_panels = [];
35285 };
35286
35287 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35288
35289     position: '', // set by wrapper (eg. north/south etc..)
35290     unrendered_panels : null,  // unrendered panels.
35291     createBody : function(){
35292         /** This region's body element 
35293         * @type Roo.Element */
35294         this.bodyEl = this.el.createChild({
35295                 tag: "div",
35296                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35297         });
35298     },
35299
35300     onRender: function(ctr, pos)
35301     {
35302         var dh = Roo.DomHelper;
35303         /** This region's container element 
35304         * @type Roo.Element */
35305         this.el = dh.append(ctr.dom, {
35306                 tag: "div",
35307                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35308             }, true);
35309         /** This region's title element 
35310         * @type Roo.Element */
35311     
35312         this.titleEl = dh.append(this.el.dom,
35313             {
35314                     tag: "div",
35315                     unselectable: "on",
35316                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35317                     children:[
35318                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35319                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35320                     ]}, true);
35321         
35322         this.titleEl.enableDisplayMode();
35323         /** This region's title text element 
35324         * @type HTMLElement */
35325         this.titleTextEl = this.titleEl.dom.firstChild;
35326         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35327         /*
35328         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35329         this.closeBtn.enableDisplayMode();
35330         this.closeBtn.on("click", this.closeClicked, this);
35331         this.closeBtn.hide();
35332     */
35333         this.createBody(this.config);
35334         if(this.config.hideWhenEmpty){
35335             this.hide();
35336             this.on("paneladded", this.validateVisibility, this);
35337             this.on("panelremoved", this.validateVisibility, this);
35338         }
35339         if(this.autoScroll){
35340             this.bodyEl.setStyle("overflow", "auto");
35341         }else{
35342             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35343         }
35344         //if(c.titlebar !== false){
35345             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35346                 this.titleEl.hide();
35347             }else{
35348                 this.titleEl.show();
35349                 if(this.config.title){
35350                     this.titleTextEl.innerHTML = this.config.title;
35351                 }
35352             }
35353         //}
35354         if(this.config.collapsed){
35355             this.collapse(true);
35356         }
35357         if(this.config.hidden){
35358             this.hide();
35359         }
35360         
35361         if (this.unrendered_panels && this.unrendered_panels.length) {
35362             for (var i =0;i< this.unrendered_panels.length; i++) {
35363                 this.add(this.unrendered_panels[i]);
35364             }
35365             this.unrendered_panels = null;
35366             
35367         }
35368         
35369     },
35370     
35371     applyConfig : function(c)
35372     {
35373         /*
35374          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35375             var dh = Roo.DomHelper;
35376             if(c.titlebar !== false){
35377                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35378                 this.collapseBtn.on("click", this.collapse, this);
35379                 this.collapseBtn.enableDisplayMode();
35380                 /*
35381                 if(c.showPin === true || this.showPin){
35382                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35383                     this.stickBtn.enableDisplayMode();
35384                     this.stickBtn.on("click", this.expand, this);
35385                     this.stickBtn.hide();
35386                 }
35387                 
35388             }
35389             */
35390             /** This region's collapsed element
35391             * @type Roo.Element */
35392             /*
35393              *
35394             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35395                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35396             ]}, true);
35397             
35398             if(c.floatable !== false){
35399                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35400                this.collapsedEl.on("click", this.collapseClick, this);
35401             }
35402
35403             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35404                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35405                    id: "message", unselectable: "on", style:{"float":"left"}});
35406                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35407              }
35408             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35409             this.expandBtn.on("click", this.expand, this);
35410             
35411         }
35412         
35413         if(this.collapseBtn){
35414             this.collapseBtn.setVisible(c.collapsible == true);
35415         }
35416         
35417         this.cmargins = c.cmargins || this.cmargins ||
35418                          (this.position == "west" || this.position == "east" ?
35419                              {top: 0, left: 2, right:2, bottom: 0} :
35420                              {top: 2, left: 0, right:0, bottom: 2});
35421         */
35422         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35423         
35424         
35425         this.bottomTabs = c.tabPosition != "top";
35426         
35427         this.autoScroll = c.autoScroll || false;
35428         
35429         
35430        
35431         
35432         this.duration = c.duration || .30;
35433         this.slideDuration = c.slideDuration || .45;
35434         this.config = c;
35435        
35436     },
35437     /**
35438      * Returns true if this region is currently visible.
35439      * @return {Boolean}
35440      */
35441     isVisible : function(){
35442         return this.visible;
35443     },
35444
35445     /**
35446      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35447      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35448      */
35449     //setCollapsedTitle : function(title){
35450     //    title = title || "&#160;";
35451      //   if(this.collapsedTitleTextEl){
35452       //      this.collapsedTitleTextEl.innerHTML = title;
35453        // }
35454     //},
35455
35456     getBox : function(){
35457         var b;
35458       //  if(!this.collapsed){
35459             b = this.el.getBox(false, true);
35460        // }else{
35461           //  b = this.collapsedEl.getBox(false, true);
35462         //}
35463         return b;
35464     },
35465
35466     getMargins : function(){
35467         return this.margins;
35468         //return this.collapsed ? this.cmargins : this.margins;
35469     },
35470 /*
35471     highlight : function(){
35472         this.el.addClass("x-layout-panel-dragover");
35473     },
35474
35475     unhighlight : function(){
35476         this.el.removeClass("x-layout-panel-dragover");
35477     },
35478 */
35479     updateBox : function(box)
35480     {
35481         if (!this.bodyEl) {
35482             return; // not rendered yet..
35483         }
35484         
35485         this.box = box;
35486         if(!this.collapsed){
35487             this.el.dom.style.left = box.x + "px";
35488             this.el.dom.style.top = box.y + "px";
35489             this.updateBody(box.width, box.height);
35490         }else{
35491             this.collapsedEl.dom.style.left = box.x + "px";
35492             this.collapsedEl.dom.style.top = box.y + "px";
35493             this.collapsedEl.setSize(box.width, box.height);
35494         }
35495         if(this.tabs){
35496             this.tabs.autoSizeTabs();
35497         }
35498     },
35499
35500     updateBody : function(w, h)
35501     {
35502         if(w !== null){
35503             this.el.setWidth(w);
35504             w -= this.el.getBorderWidth("rl");
35505             if(this.config.adjustments){
35506                 w += this.config.adjustments[0];
35507             }
35508         }
35509         if(h !== null && h > 0){
35510             this.el.setHeight(h);
35511             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35512             h -= this.el.getBorderWidth("tb");
35513             if(this.config.adjustments){
35514                 h += this.config.adjustments[1];
35515             }
35516             this.bodyEl.setHeight(h);
35517             if(this.tabs){
35518                 h = this.tabs.syncHeight(h);
35519             }
35520         }
35521         if(this.panelSize){
35522             w = w !== null ? w : this.panelSize.width;
35523             h = h !== null ? h : this.panelSize.height;
35524         }
35525         if(this.activePanel){
35526             var el = this.activePanel.getEl();
35527             w = w !== null ? w : el.getWidth();
35528             h = h !== null ? h : el.getHeight();
35529             this.panelSize = {width: w, height: h};
35530             this.activePanel.setSize(w, h);
35531         }
35532         if(Roo.isIE && this.tabs){
35533             this.tabs.el.repaint();
35534         }
35535     },
35536
35537     /**
35538      * Returns the container element for this region.
35539      * @return {Roo.Element}
35540      */
35541     getEl : function(){
35542         return this.el;
35543     },
35544
35545     /**
35546      * Hides this region.
35547      */
35548     hide : function(){
35549         //if(!this.collapsed){
35550             this.el.dom.style.left = "-2000px";
35551             this.el.hide();
35552         //}else{
35553          //   this.collapsedEl.dom.style.left = "-2000px";
35554          //   this.collapsedEl.hide();
35555        // }
35556         this.visible = false;
35557         this.fireEvent("visibilitychange", this, false);
35558     },
35559
35560     /**
35561      * Shows this region if it was previously hidden.
35562      */
35563     show : function(){
35564         //if(!this.collapsed){
35565             this.el.show();
35566         //}else{
35567         //    this.collapsedEl.show();
35568        // }
35569         this.visible = true;
35570         this.fireEvent("visibilitychange", this, true);
35571     },
35572 /*
35573     closeClicked : function(){
35574         if(this.activePanel){
35575             this.remove(this.activePanel);
35576         }
35577     },
35578
35579     collapseClick : function(e){
35580         if(this.isSlid){
35581            e.stopPropagation();
35582            this.slideIn();
35583         }else{
35584            e.stopPropagation();
35585            this.slideOut();
35586         }
35587     },
35588 */
35589     /**
35590      * Collapses this region.
35591      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35592      */
35593     /*
35594     collapse : function(skipAnim, skipCheck = false){
35595         if(this.collapsed) {
35596             return;
35597         }
35598         
35599         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35600             
35601             this.collapsed = true;
35602             if(this.split){
35603                 this.split.el.hide();
35604             }
35605             if(this.config.animate && skipAnim !== true){
35606                 this.fireEvent("invalidated", this);
35607                 this.animateCollapse();
35608             }else{
35609                 this.el.setLocation(-20000,-20000);
35610                 this.el.hide();
35611                 this.collapsedEl.show();
35612                 this.fireEvent("collapsed", this);
35613                 this.fireEvent("invalidated", this);
35614             }
35615         }
35616         
35617     },
35618 */
35619     animateCollapse : function(){
35620         // overridden
35621     },
35622
35623     /**
35624      * Expands this region if it was previously collapsed.
35625      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35626      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35627      */
35628     /*
35629     expand : function(e, skipAnim){
35630         if(e) {
35631             e.stopPropagation();
35632         }
35633         if(!this.collapsed || this.el.hasActiveFx()) {
35634             return;
35635         }
35636         if(this.isSlid){
35637             this.afterSlideIn();
35638             skipAnim = true;
35639         }
35640         this.collapsed = false;
35641         if(this.config.animate && skipAnim !== true){
35642             this.animateExpand();
35643         }else{
35644             this.el.show();
35645             if(this.split){
35646                 this.split.el.show();
35647             }
35648             this.collapsedEl.setLocation(-2000,-2000);
35649             this.collapsedEl.hide();
35650             this.fireEvent("invalidated", this);
35651             this.fireEvent("expanded", this);
35652         }
35653     },
35654 */
35655     animateExpand : function(){
35656         // overridden
35657     },
35658
35659     initTabs : function()
35660     {
35661         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35662         
35663         var ts = new Roo.bootstrap.panel.Tabs({
35664                 el: this.bodyEl.dom,
35665                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35666                 disableTooltips: this.config.disableTabTips,
35667                 toolbar : this.config.toolbar
35668             });
35669         
35670         if(this.config.hideTabs){
35671             ts.stripWrap.setDisplayed(false);
35672         }
35673         this.tabs = ts;
35674         ts.resizeTabs = this.config.resizeTabs === true;
35675         ts.minTabWidth = this.config.minTabWidth || 40;
35676         ts.maxTabWidth = this.config.maxTabWidth || 250;
35677         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35678         ts.monitorResize = false;
35679         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35680         ts.bodyEl.addClass('roo-layout-tabs-body');
35681         this.panels.each(this.initPanelAsTab, this);
35682     },
35683
35684     initPanelAsTab : function(panel){
35685         var ti = this.tabs.addTab(
35686             panel.getEl().id,
35687             panel.getTitle(),
35688             null,
35689             this.config.closeOnTab && panel.isClosable(),
35690             panel.tpl
35691         );
35692         if(panel.tabTip !== undefined){
35693             ti.setTooltip(panel.tabTip);
35694         }
35695         ti.on("activate", function(){
35696               this.setActivePanel(panel);
35697         }, this);
35698         
35699         if(this.config.closeOnTab){
35700             ti.on("beforeclose", function(t, e){
35701                 e.cancel = true;
35702                 this.remove(panel);
35703             }, this);
35704         }
35705         
35706         panel.tabItem = ti;
35707         
35708         return ti;
35709     },
35710
35711     updatePanelTitle : function(panel, title)
35712     {
35713         if(this.activePanel == panel){
35714             this.updateTitle(title);
35715         }
35716         if(this.tabs){
35717             var ti = this.tabs.getTab(panel.getEl().id);
35718             ti.setText(title);
35719             if(panel.tabTip !== undefined){
35720                 ti.setTooltip(panel.tabTip);
35721             }
35722         }
35723     },
35724
35725     updateTitle : function(title){
35726         if(this.titleTextEl && !this.config.title){
35727             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35728         }
35729     },
35730
35731     setActivePanel : function(panel)
35732     {
35733         panel = this.getPanel(panel);
35734         if(this.activePanel && this.activePanel != panel){
35735             if(this.activePanel.setActiveState(false) === false){
35736                 return;
35737             }
35738         }
35739         this.activePanel = panel;
35740         panel.setActiveState(true);
35741         if(this.panelSize){
35742             panel.setSize(this.panelSize.width, this.panelSize.height);
35743         }
35744         if(this.closeBtn){
35745             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35746         }
35747         this.updateTitle(panel.getTitle());
35748         if(this.tabs){
35749             this.fireEvent("invalidated", this);
35750         }
35751         this.fireEvent("panelactivated", this, panel);
35752     },
35753
35754     /**
35755      * Shows the specified panel.
35756      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35757      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35758      */
35759     showPanel : function(panel)
35760     {
35761         panel = this.getPanel(panel);
35762         if(panel){
35763             if(this.tabs){
35764                 var tab = this.tabs.getTab(panel.getEl().id);
35765                 if(tab.isHidden()){
35766                     this.tabs.unhideTab(tab.id);
35767                 }
35768                 tab.activate();
35769             }else{
35770                 this.setActivePanel(panel);
35771             }
35772         }
35773         return panel;
35774     },
35775
35776     /**
35777      * Get the active panel for this region.
35778      * @return {Roo.ContentPanel} The active panel or null
35779      */
35780     getActivePanel : function(){
35781         return this.activePanel;
35782     },
35783
35784     validateVisibility : function(){
35785         if(this.panels.getCount() < 1){
35786             this.updateTitle("&#160;");
35787             this.closeBtn.hide();
35788             this.hide();
35789         }else{
35790             if(!this.isVisible()){
35791                 this.show();
35792             }
35793         }
35794     },
35795
35796     /**
35797      * Adds the passed ContentPanel(s) to this region.
35798      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35799      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35800      */
35801     add : function(panel)
35802     {
35803         if(arguments.length > 1){
35804             for(var i = 0, len = arguments.length; i < len; i++) {
35805                 this.add(arguments[i]);
35806             }
35807             return null;
35808         }
35809         
35810         // if we have not been rendered yet, then we can not really do much of this..
35811         if (!this.bodyEl) {
35812             this.unrendered_panels.push(panel);
35813             return panel;
35814         }
35815         
35816         
35817         
35818         
35819         if(this.hasPanel(panel)){
35820             this.showPanel(panel);
35821             return panel;
35822         }
35823         panel.setRegion(this);
35824         this.panels.add(panel);
35825        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35826             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35827             // and hide them... ???
35828             this.bodyEl.dom.appendChild(panel.getEl().dom);
35829             if(panel.background !== true){
35830                 this.setActivePanel(panel);
35831             }
35832             this.fireEvent("paneladded", this, panel);
35833             return panel;
35834         }
35835         */
35836         if(!this.tabs){
35837             this.initTabs();
35838         }else{
35839             this.initPanelAsTab(panel);
35840         }
35841         
35842         
35843         if(panel.background !== true){
35844             this.tabs.activate(panel.getEl().id);
35845         }
35846         this.fireEvent("paneladded", this, panel);
35847         return panel;
35848     },
35849
35850     /**
35851      * Hides the tab for the specified panel.
35852      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35853      */
35854     hidePanel : function(panel){
35855         if(this.tabs && (panel = this.getPanel(panel))){
35856             this.tabs.hideTab(panel.getEl().id);
35857         }
35858     },
35859
35860     /**
35861      * Unhides the tab for a previously hidden panel.
35862      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35863      */
35864     unhidePanel : function(panel){
35865         if(this.tabs && (panel = this.getPanel(panel))){
35866             this.tabs.unhideTab(panel.getEl().id);
35867         }
35868     },
35869
35870     clearPanels : function(){
35871         while(this.panels.getCount() > 0){
35872              this.remove(this.panels.first());
35873         }
35874     },
35875
35876     /**
35877      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35878      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35879      * @param {Boolean} preservePanel Overrides the config preservePanel option
35880      * @return {Roo.ContentPanel} The panel that was removed
35881      */
35882     remove : function(panel, preservePanel)
35883     {
35884         panel = this.getPanel(panel);
35885         if(!panel){
35886             return null;
35887         }
35888         var e = {};
35889         this.fireEvent("beforeremove", this, panel, e);
35890         if(e.cancel === true){
35891             return null;
35892         }
35893         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35894         var panelId = panel.getId();
35895         this.panels.removeKey(panelId);
35896         if(preservePanel){
35897             document.body.appendChild(panel.getEl().dom);
35898         }
35899         if(this.tabs){
35900             this.tabs.removeTab(panel.getEl().id);
35901         }else if (!preservePanel){
35902             this.bodyEl.dom.removeChild(panel.getEl().dom);
35903         }
35904         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35905             var p = this.panels.first();
35906             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35907             tempEl.appendChild(p.getEl().dom);
35908             this.bodyEl.update("");
35909             this.bodyEl.dom.appendChild(p.getEl().dom);
35910             tempEl = null;
35911             this.updateTitle(p.getTitle());
35912             this.tabs = null;
35913             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35914             this.setActivePanel(p);
35915         }
35916         panel.setRegion(null);
35917         if(this.activePanel == panel){
35918             this.activePanel = null;
35919         }
35920         if(this.config.autoDestroy !== false && preservePanel !== true){
35921             try{panel.destroy();}catch(e){}
35922         }
35923         this.fireEvent("panelremoved", this, panel);
35924         return panel;
35925     },
35926
35927     /**
35928      * Returns the TabPanel component used by this region
35929      * @return {Roo.TabPanel}
35930      */
35931     getTabs : function(){
35932         return this.tabs;
35933     },
35934
35935     createTool : function(parentEl, className){
35936         var btn = Roo.DomHelper.append(parentEl, {
35937             tag: "div",
35938             cls: "x-layout-tools-button",
35939             children: [ {
35940                 tag: "div",
35941                 cls: "roo-layout-tools-button-inner " + className,
35942                 html: "&#160;"
35943             }]
35944         }, true);
35945         btn.addClassOnOver("roo-layout-tools-button-over");
35946         return btn;
35947     }
35948 });/*
35949  * Based on:
35950  * Ext JS Library 1.1.1
35951  * Copyright(c) 2006-2007, Ext JS, LLC.
35952  *
35953  * Originally Released Under LGPL - original licence link has changed is not relivant.
35954  *
35955  * Fork - LGPL
35956  * <script type="text/javascript">
35957  */
35958  
35959
35960
35961 /**
35962  * @class Roo.SplitLayoutRegion
35963  * @extends Roo.LayoutRegion
35964  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35965  */
35966 Roo.bootstrap.layout.Split = function(config){
35967     this.cursor = config.cursor;
35968     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35969 };
35970
35971 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35972 {
35973     splitTip : "Drag to resize.",
35974     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35975     useSplitTips : false,
35976
35977     applyConfig : function(config){
35978         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35979     },
35980     
35981     onRender : function(ctr,pos) {
35982         
35983         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35984         if(!this.config.split){
35985             return;
35986         }
35987         if(!this.split){
35988             
35989             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35990                             tag: "div",
35991                             id: this.el.id + "-split",
35992                             cls: "roo-layout-split roo-layout-split-"+this.position,
35993                             html: "&#160;"
35994             });
35995             /** The SplitBar for this region 
35996             * @type Roo.SplitBar */
35997             // does not exist yet...
35998             Roo.log([this.position, this.orientation]);
35999             
36000             this.split = new Roo.bootstrap.SplitBar({
36001                 dragElement : splitEl,
36002                 resizingElement: this.el,
36003                 orientation : this.orientation
36004             });
36005             
36006             this.split.on("moved", this.onSplitMove, this);
36007             this.split.useShim = this.config.useShim === true;
36008             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36009             if(this.useSplitTips){
36010                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36011             }
36012             //if(config.collapsible){
36013             //    this.split.el.on("dblclick", this.collapse,  this);
36014             //}
36015         }
36016         if(typeof this.config.minSize != "undefined"){
36017             this.split.minSize = this.config.minSize;
36018         }
36019         if(typeof this.config.maxSize != "undefined"){
36020             this.split.maxSize = this.config.maxSize;
36021         }
36022         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36023             this.hideSplitter();
36024         }
36025         
36026     },
36027
36028     getHMaxSize : function(){
36029          var cmax = this.config.maxSize || 10000;
36030          var center = this.mgr.getRegion("center");
36031          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36032     },
36033
36034     getVMaxSize : function(){
36035          var cmax = this.config.maxSize || 10000;
36036          var center = this.mgr.getRegion("center");
36037          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36038     },
36039
36040     onSplitMove : function(split, newSize){
36041         this.fireEvent("resized", this, newSize);
36042     },
36043     
36044     /** 
36045      * Returns the {@link Roo.SplitBar} for this region.
36046      * @return {Roo.SplitBar}
36047      */
36048     getSplitBar : function(){
36049         return this.split;
36050     },
36051     
36052     hide : function(){
36053         this.hideSplitter();
36054         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36055     },
36056
36057     hideSplitter : function(){
36058         if(this.split){
36059             this.split.el.setLocation(-2000,-2000);
36060             this.split.el.hide();
36061         }
36062     },
36063
36064     show : function(){
36065         if(this.split){
36066             this.split.el.show();
36067         }
36068         Roo.bootstrap.layout.Split.superclass.show.call(this);
36069     },
36070     
36071     beforeSlide: function(){
36072         if(Roo.isGecko){// firefox overflow auto bug workaround
36073             this.bodyEl.clip();
36074             if(this.tabs) {
36075                 this.tabs.bodyEl.clip();
36076             }
36077             if(this.activePanel){
36078                 this.activePanel.getEl().clip();
36079                 
36080                 if(this.activePanel.beforeSlide){
36081                     this.activePanel.beforeSlide();
36082                 }
36083             }
36084         }
36085     },
36086     
36087     afterSlide : function(){
36088         if(Roo.isGecko){// firefox overflow auto bug workaround
36089             this.bodyEl.unclip();
36090             if(this.tabs) {
36091                 this.tabs.bodyEl.unclip();
36092             }
36093             if(this.activePanel){
36094                 this.activePanel.getEl().unclip();
36095                 if(this.activePanel.afterSlide){
36096                     this.activePanel.afterSlide();
36097                 }
36098             }
36099         }
36100     },
36101
36102     initAutoHide : function(){
36103         if(this.autoHide !== false){
36104             if(!this.autoHideHd){
36105                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36106                 this.autoHideHd = {
36107                     "mouseout": function(e){
36108                         if(!e.within(this.el, true)){
36109                             st.delay(500);
36110                         }
36111                     },
36112                     "mouseover" : function(e){
36113                         st.cancel();
36114                     },
36115                     scope : this
36116                 };
36117             }
36118             this.el.on(this.autoHideHd);
36119         }
36120     },
36121
36122     clearAutoHide : function(){
36123         if(this.autoHide !== false){
36124             this.el.un("mouseout", this.autoHideHd.mouseout);
36125             this.el.un("mouseover", this.autoHideHd.mouseover);
36126         }
36127     },
36128
36129     clearMonitor : function(){
36130         Roo.get(document).un("click", this.slideInIf, this);
36131     },
36132
36133     // these names are backwards but not changed for compat
36134     slideOut : function(){
36135         if(this.isSlid || this.el.hasActiveFx()){
36136             return;
36137         }
36138         this.isSlid = true;
36139         if(this.collapseBtn){
36140             this.collapseBtn.hide();
36141         }
36142         this.closeBtnState = this.closeBtn.getStyle('display');
36143         this.closeBtn.hide();
36144         if(this.stickBtn){
36145             this.stickBtn.show();
36146         }
36147         this.el.show();
36148         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36149         this.beforeSlide();
36150         this.el.setStyle("z-index", 10001);
36151         this.el.slideIn(this.getSlideAnchor(), {
36152             callback: function(){
36153                 this.afterSlide();
36154                 this.initAutoHide();
36155                 Roo.get(document).on("click", this.slideInIf, this);
36156                 this.fireEvent("slideshow", this);
36157             },
36158             scope: this,
36159             block: true
36160         });
36161     },
36162
36163     afterSlideIn : function(){
36164         this.clearAutoHide();
36165         this.isSlid = false;
36166         this.clearMonitor();
36167         this.el.setStyle("z-index", "");
36168         if(this.collapseBtn){
36169             this.collapseBtn.show();
36170         }
36171         this.closeBtn.setStyle('display', this.closeBtnState);
36172         if(this.stickBtn){
36173             this.stickBtn.hide();
36174         }
36175         this.fireEvent("slidehide", this);
36176     },
36177
36178     slideIn : function(cb){
36179         if(!this.isSlid || this.el.hasActiveFx()){
36180             Roo.callback(cb);
36181             return;
36182         }
36183         this.isSlid = false;
36184         this.beforeSlide();
36185         this.el.slideOut(this.getSlideAnchor(), {
36186             callback: function(){
36187                 this.el.setLeftTop(-10000, -10000);
36188                 this.afterSlide();
36189                 this.afterSlideIn();
36190                 Roo.callback(cb);
36191             },
36192             scope: this,
36193             block: true
36194         });
36195     },
36196     
36197     slideInIf : function(e){
36198         if(!e.within(this.el)){
36199             this.slideIn();
36200         }
36201     },
36202
36203     animateCollapse : function(){
36204         this.beforeSlide();
36205         this.el.setStyle("z-index", 20000);
36206         var anchor = this.getSlideAnchor();
36207         this.el.slideOut(anchor, {
36208             callback : function(){
36209                 this.el.setStyle("z-index", "");
36210                 this.collapsedEl.slideIn(anchor, {duration:.3});
36211                 this.afterSlide();
36212                 this.el.setLocation(-10000,-10000);
36213                 this.el.hide();
36214                 this.fireEvent("collapsed", this);
36215             },
36216             scope: this,
36217             block: true
36218         });
36219     },
36220
36221     animateExpand : function(){
36222         this.beforeSlide();
36223         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36224         this.el.setStyle("z-index", 20000);
36225         this.collapsedEl.hide({
36226             duration:.1
36227         });
36228         this.el.slideIn(this.getSlideAnchor(), {
36229             callback : function(){
36230                 this.el.setStyle("z-index", "");
36231                 this.afterSlide();
36232                 if(this.split){
36233                     this.split.el.show();
36234                 }
36235                 this.fireEvent("invalidated", this);
36236                 this.fireEvent("expanded", this);
36237             },
36238             scope: this,
36239             block: true
36240         });
36241     },
36242
36243     anchors : {
36244         "west" : "left",
36245         "east" : "right",
36246         "north" : "top",
36247         "south" : "bottom"
36248     },
36249
36250     sanchors : {
36251         "west" : "l",
36252         "east" : "r",
36253         "north" : "t",
36254         "south" : "b"
36255     },
36256
36257     canchors : {
36258         "west" : "tl-tr",
36259         "east" : "tr-tl",
36260         "north" : "tl-bl",
36261         "south" : "bl-tl"
36262     },
36263
36264     getAnchor : function(){
36265         return this.anchors[this.position];
36266     },
36267
36268     getCollapseAnchor : function(){
36269         return this.canchors[this.position];
36270     },
36271
36272     getSlideAnchor : function(){
36273         return this.sanchors[this.position];
36274     },
36275
36276     getAlignAdj : function(){
36277         var cm = this.cmargins;
36278         switch(this.position){
36279             case "west":
36280                 return [0, 0];
36281             break;
36282             case "east":
36283                 return [0, 0];
36284             break;
36285             case "north":
36286                 return [0, 0];
36287             break;
36288             case "south":
36289                 return [0, 0];
36290             break;
36291         }
36292     },
36293
36294     getExpandAdj : function(){
36295         var c = this.collapsedEl, cm = this.cmargins;
36296         switch(this.position){
36297             case "west":
36298                 return [-(cm.right+c.getWidth()+cm.left), 0];
36299             break;
36300             case "east":
36301                 return [cm.right+c.getWidth()+cm.left, 0];
36302             break;
36303             case "north":
36304                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36305             break;
36306             case "south":
36307                 return [0, cm.top+cm.bottom+c.getHeight()];
36308             break;
36309         }
36310     }
36311 });/*
36312  * Based on:
36313  * Ext JS Library 1.1.1
36314  * Copyright(c) 2006-2007, Ext JS, LLC.
36315  *
36316  * Originally Released Under LGPL - original licence link has changed is not relivant.
36317  *
36318  * Fork - LGPL
36319  * <script type="text/javascript">
36320  */
36321 /*
36322  * These classes are private internal classes
36323  */
36324 Roo.bootstrap.layout.Center = function(config){
36325     config.region = "center";
36326     Roo.bootstrap.layout.Region.call(this, config);
36327     this.visible = true;
36328     this.minWidth = config.minWidth || 20;
36329     this.minHeight = config.minHeight || 20;
36330 };
36331
36332 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36333     hide : function(){
36334         // center panel can't be hidden
36335     },
36336     
36337     show : function(){
36338         // center panel can't be hidden
36339     },
36340     
36341     getMinWidth: function(){
36342         return this.minWidth;
36343     },
36344     
36345     getMinHeight: function(){
36346         return this.minHeight;
36347     }
36348 });
36349
36350
36351
36352
36353  
36354
36355
36356
36357
36358
36359 Roo.bootstrap.layout.North = function(config)
36360 {
36361     config.region = 'north';
36362     config.cursor = 'n-resize';
36363     
36364     Roo.bootstrap.layout.Split.call(this, config);
36365     
36366     
36367     if(this.split){
36368         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36369         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36370         this.split.el.addClass("roo-layout-split-v");
36371     }
36372     var size = config.initialSize || config.height;
36373     if(typeof size != "undefined"){
36374         this.el.setHeight(size);
36375     }
36376 };
36377 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36378 {
36379     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36380     
36381     
36382     
36383     getBox : function(){
36384         if(this.collapsed){
36385             return this.collapsedEl.getBox();
36386         }
36387         var box = this.el.getBox();
36388         if(this.split){
36389             box.height += this.split.el.getHeight();
36390         }
36391         return box;
36392     },
36393     
36394     updateBox : function(box){
36395         if(this.split && !this.collapsed){
36396             box.height -= this.split.el.getHeight();
36397             this.split.el.setLeft(box.x);
36398             this.split.el.setTop(box.y+box.height);
36399             this.split.el.setWidth(box.width);
36400         }
36401         if(this.collapsed){
36402             this.updateBody(box.width, null);
36403         }
36404         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36405     }
36406 });
36407
36408
36409
36410
36411
36412 Roo.bootstrap.layout.South = function(config){
36413     config.region = 'south';
36414     config.cursor = 's-resize';
36415     Roo.bootstrap.layout.Split.call(this, config);
36416     if(this.split){
36417         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36418         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36419         this.split.el.addClass("roo-layout-split-v");
36420     }
36421     var size = config.initialSize || config.height;
36422     if(typeof size != "undefined"){
36423         this.el.setHeight(size);
36424     }
36425 };
36426
36427 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36428     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36429     getBox : function(){
36430         if(this.collapsed){
36431             return this.collapsedEl.getBox();
36432         }
36433         var box = this.el.getBox();
36434         if(this.split){
36435             var sh = this.split.el.getHeight();
36436             box.height += sh;
36437             box.y -= sh;
36438         }
36439         return box;
36440     },
36441     
36442     updateBox : function(box){
36443         if(this.split && !this.collapsed){
36444             var sh = this.split.el.getHeight();
36445             box.height -= sh;
36446             box.y += sh;
36447             this.split.el.setLeft(box.x);
36448             this.split.el.setTop(box.y-sh);
36449             this.split.el.setWidth(box.width);
36450         }
36451         if(this.collapsed){
36452             this.updateBody(box.width, null);
36453         }
36454         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36455     }
36456 });
36457
36458 Roo.bootstrap.layout.East = function(config){
36459     config.region = "east";
36460     config.cursor = "e-resize";
36461     Roo.bootstrap.layout.Split.call(this, config);
36462     if(this.split){
36463         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36464         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36465         this.split.el.addClass("roo-layout-split-h");
36466     }
36467     var size = config.initialSize || config.width;
36468     if(typeof size != "undefined"){
36469         this.el.setWidth(size);
36470     }
36471 };
36472 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36473     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36474     getBox : function(){
36475         if(this.collapsed){
36476             return this.collapsedEl.getBox();
36477         }
36478         var box = this.el.getBox();
36479         if(this.split){
36480             var sw = this.split.el.getWidth();
36481             box.width += sw;
36482             box.x -= sw;
36483         }
36484         return box;
36485     },
36486
36487     updateBox : function(box){
36488         if(this.split && !this.collapsed){
36489             var sw = this.split.el.getWidth();
36490             box.width -= sw;
36491             this.split.el.setLeft(box.x);
36492             this.split.el.setTop(box.y);
36493             this.split.el.setHeight(box.height);
36494             box.x += sw;
36495         }
36496         if(this.collapsed){
36497             this.updateBody(null, box.height);
36498         }
36499         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36500     }
36501 });
36502
36503 Roo.bootstrap.layout.West = function(config){
36504     config.region = "west";
36505     config.cursor = "w-resize";
36506     
36507     Roo.bootstrap.layout.Split.call(this, config);
36508     if(this.split){
36509         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36510         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36511         this.split.el.addClass("roo-layout-split-h");
36512     }
36513     
36514 };
36515 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36516     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36517     
36518     onRender: function(ctr, pos)
36519     {
36520         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36521         var size = this.config.initialSize || this.config.width;
36522         if(typeof size != "undefined"){
36523             this.el.setWidth(size);
36524         }
36525     },
36526     
36527     getBox : function(){
36528         if(this.collapsed){
36529             return this.collapsedEl.getBox();
36530         }
36531         var box = this.el.getBox();
36532         if(this.split){
36533             box.width += this.split.el.getWidth();
36534         }
36535         return box;
36536     },
36537     
36538     updateBox : function(box){
36539         if(this.split && !this.collapsed){
36540             var sw = this.split.el.getWidth();
36541             box.width -= sw;
36542             this.split.el.setLeft(box.x+box.width);
36543             this.split.el.setTop(box.y);
36544             this.split.el.setHeight(box.height);
36545         }
36546         if(this.collapsed){
36547             this.updateBody(null, box.height);
36548         }
36549         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36550     }
36551 });
36552 Roo.namespace("Roo.bootstrap.panel");/*
36553  * Based on:
36554  * Ext JS Library 1.1.1
36555  * Copyright(c) 2006-2007, Ext JS, LLC.
36556  *
36557  * Originally Released Under LGPL - original licence link has changed is not relivant.
36558  *
36559  * Fork - LGPL
36560  * <script type="text/javascript">
36561  */
36562 /**
36563  * @class Roo.ContentPanel
36564  * @extends Roo.util.Observable
36565  * A basic ContentPanel element.
36566  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36567  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36568  * @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
36569  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36570  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36571  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36572  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36573  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36574  * @cfg {String} title          The title for this panel
36575  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36576  * @cfg {String} url            Calls {@link #setUrl} with this value
36577  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36578  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36579  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36580  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36581  * @cfg {Boolean} badges render the badges
36582
36583  * @constructor
36584  * Create a new ContentPanel.
36585  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36586  * @param {String/Object} config A string to set only the title or a config object
36587  * @param {String} content (optional) Set the HTML content for this panel
36588  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36589  */
36590 Roo.bootstrap.panel.Content = function( config){
36591     
36592     this.tpl = config.tpl || false;
36593     
36594     var el = config.el;
36595     var content = config.content;
36596
36597     if(config.autoCreate){ // xtype is available if this is called from factory
36598         el = Roo.id();
36599     }
36600     this.el = Roo.get(el);
36601     if(!this.el && config && config.autoCreate){
36602         if(typeof config.autoCreate == "object"){
36603             if(!config.autoCreate.id){
36604                 config.autoCreate.id = config.id||el;
36605             }
36606             this.el = Roo.DomHelper.append(document.body,
36607                         config.autoCreate, true);
36608         }else{
36609             var elcfg =  {   tag: "div",
36610                             cls: "roo-layout-inactive-content",
36611                             id: config.id||el
36612                             };
36613             if (config.html) {
36614                 elcfg.html = config.html;
36615                 
36616             }
36617                         
36618             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36619         }
36620     } 
36621     this.closable = false;
36622     this.loaded = false;
36623     this.active = false;
36624    
36625       
36626     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36627         
36628         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36629         
36630         this.wrapEl = this.el; //this.el.wrap();
36631         var ti = [];
36632         if (config.toolbar.items) {
36633             ti = config.toolbar.items ;
36634             delete config.toolbar.items ;
36635         }
36636         
36637         var nitems = [];
36638         this.toolbar.render(this.wrapEl, 'before');
36639         for(var i =0;i < ti.length;i++) {
36640           //  Roo.log(['add child', items[i]]);
36641             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36642         }
36643         this.toolbar.items = nitems;
36644         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36645         delete config.toolbar;
36646         
36647     }
36648     /*
36649     // xtype created footer. - not sure if will work as we normally have to render first..
36650     if (this.footer && !this.footer.el && this.footer.xtype) {
36651         if (!this.wrapEl) {
36652             this.wrapEl = this.el.wrap();
36653         }
36654     
36655         this.footer.container = this.wrapEl.createChild();
36656          
36657         this.footer = Roo.factory(this.footer, Roo);
36658         
36659     }
36660     */
36661     
36662      if(typeof config == "string"){
36663         this.title = config;
36664     }else{
36665         Roo.apply(this, config);
36666     }
36667     
36668     if(this.resizeEl){
36669         this.resizeEl = Roo.get(this.resizeEl, true);
36670     }else{
36671         this.resizeEl = this.el;
36672     }
36673     // handle view.xtype
36674     
36675  
36676     
36677     
36678     this.addEvents({
36679         /**
36680          * @event activate
36681          * Fires when this panel is activated. 
36682          * @param {Roo.ContentPanel} this
36683          */
36684         "activate" : true,
36685         /**
36686          * @event deactivate
36687          * Fires when this panel is activated. 
36688          * @param {Roo.ContentPanel} this
36689          */
36690         "deactivate" : true,
36691
36692         /**
36693          * @event resize
36694          * Fires when this panel is resized if fitToFrame is true.
36695          * @param {Roo.ContentPanel} this
36696          * @param {Number} width The width after any component adjustments
36697          * @param {Number} height The height after any component adjustments
36698          */
36699         "resize" : true,
36700         
36701          /**
36702          * @event render
36703          * Fires when this tab is created
36704          * @param {Roo.ContentPanel} this
36705          */
36706         "render" : true
36707         
36708         
36709         
36710     });
36711     
36712
36713     
36714     
36715     if(this.autoScroll){
36716         this.resizeEl.setStyle("overflow", "auto");
36717     } else {
36718         // fix randome scrolling
36719         //this.el.on('scroll', function() {
36720         //    Roo.log('fix random scolling');
36721         //    this.scrollTo('top',0); 
36722         //});
36723     }
36724     content = content || this.content;
36725     if(content){
36726         this.setContent(content);
36727     }
36728     if(config && config.url){
36729         this.setUrl(this.url, this.params, this.loadOnce);
36730     }
36731     
36732     
36733     
36734     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36735     
36736     if (this.view && typeof(this.view.xtype) != 'undefined') {
36737         this.view.el = this.el.appendChild(document.createElement("div"));
36738         this.view = Roo.factory(this.view); 
36739         this.view.render  &&  this.view.render(false, '');  
36740     }
36741     
36742     
36743     this.fireEvent('render', this);
36744 };
36745
36746 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36747     
36748     tabTip : '',
36749     
36750     setRegion : function(region){
36751         this.region = region;
36752         this.setActiveClass(region && !this.background);
36753     },
36754     
36755     
36756     setActiveClass: function(state)
36757     {
36758         if(state){
36759            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36760            this.el.setStyle('position','relative');
36761         }else{
36762            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36763            this.el.setStyle('position', 'absolute');
36764         } 
36765     },
36766     
36767     /**
36768      * Returns the toolbar for this Panel if one was configured. 
36769      * @return {Roo.Toolbar} 
36770      */
36771     getToolbar : function(){
36772         return this.toolbar;
36773     },
36774     
36775     setActiveState : function(active)
36776     {
36777         this.active = active;
36778         this.setActiveClass(active);
36779         if(!active){
36780             if(this.fireEvent("deactivate", this) === false){
36781                 return false;
36782             }
36783             return true;
36784         }
36785         this.fireEvent("activate", this);
36786         return true;
36787     },
36788     /**
36789      * Updates this panel's element
36790      * @param {String} content The new content
36791      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36792     */
36793     setContent : function(content, loadScripts){
36794         this.el.update(content, loadScripts);
36795     },
36796
36797     ignoreResize : function(w, h){
36798         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36799             return true;
36800         }else{
36801             this.lastSize = {width: w, height: h};
36802             return false;
36803         }
36804     },
36805     /**
36806      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36807      * @return {Roo.UpdateManager} The UpdateManager
36808      */
36809     getUpdateManager : function(){
36810         return this.el.getUpdateManager();
36811     },
36812      /**
36813      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36814      * @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:
36815 <pre><code>
36816 panel.load({
36817     url: "your-url.php",
36818     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36819     callback: yourFunction,
36820     scope: yourObject, //(optional scope)
36821     discardUrl: false,
36822     nocache: false,
36823     text: "Loading...",
36824     timeout: 30,
36825     scripts: false
36826 });
36827 </code></pre>
36828      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36829      * 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.
36830      * @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}
36831      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36832      * @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.
36833      * @return {Roo.ContentPanel} this
36834      */
36835     load : function(){
36836         var um = this.el.getUpdateManager();
36837         um.update.apply(um, arguments);
36838         return this;
36839     },
36840
36841
36842     /**
36843      * 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.
36844      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36845      * @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)
36846      * @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)
36847      * @return {Roo.UpdateManager} The UpdateManager
36848      */
36849     setUrl : function(url, params, loadOnce){
36850         if(this.refreshDelegate){
36851             this.removeListener("activate", this.refreshDelegate);
36852         }
36853         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36854         this.on("activate", this.refreshDelegate);
36855         return this.el.getUpdateManager();
36856     },
36857     
36858     _handleRefresh : function(url, params, loadOnce){
36859         if(!loadOnce || !this.loaded){
36860             var updater = this.el.getUpdateManager();
36861             updater.update(url, params, this._setLoaded.createDelegate(this));
36862         }
36863     },
36864     
36865     _setLoaded : function(){
36866         this.loaded = true;
36867     }, 
36868     
36869     /**
36870      * Returns this panel's id
36871      * @return {String} 
36872      */
36873     getId : function(){
36874         return this.el.id;
36875     },
36876     
36877     /** 
36878      * Returns this panel's element - used by regiosn to add.
36879      * @return {Roo.Element} 
36880      */
36881     getEl : function(){
36882         return this.wrapEl || this.el;
36883     },
36884     
36885    
36886     
36887     adjustForComponents : function(width, height)
36888     {
36889         //Roo.log('adjustForComponents ');
36890         if(this.resizeEl != this.el){
36891             width -= this.el.getFrameWidth('lr');
36892             height -= this.el.getFrameWidth('tb');
36893         }
36894         if(this.toolbar){
36895             var te = this.toolbar.getEl();
36896             te.setWidth(width);
36897             height -= te.getHeight();
36898         }
36899         if(this.footer){
36900             var te = this.footer.getEl();
36901             te.setWidth(width);
36902             height -= te.getHeight();
36903         }
36904         
36905         
36906         if(this.adjustments){
36907             width += this.adjustments[0];
36908             height += this.adjustments[1];
36909         }
36910         return {"width": width, "height": height};
36911     },
36912     
36913     setSize : function(width, height){
36914         if(this.fitToFrame && !this.ignoreResize(width, height)){
36915             if(this.fitContainer && this.resizeEl != this.el){
36916                 this.el.setSize(width, height);
36917             }
36918             var size = this.adjustForComponents(width, height);
36919             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36920             this.fireEvent('resize', this, size.width, size.height);
36921         }
36922     },
36923     
36924     /**
36925      * Returns this panel's title
36926      * @return {String} 
36927      */
36928     getTitle : function(){
36929         
36930         if (typeof(this.title) != 'object') {
36931             return this.title;
36932         }
36933         
36934         var t = '';
36935         for (var k in this.title) {
36936             if (!this.title.hasOwnProperty(k)) {
36937                 continue;
36938             }
36939             
36940             if (k.indexOf('-') >= 0) {
36941                 var s = k.split('-');
36942                 for (var i = 0; i<s.length; i++) {
36943                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36944                 }
36945             } else {
36946                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36947             }
36948         }
36949         return t;
36950     },
36951     
36952     /**
36953      * Set this panel's title
36954      * @param {String} title
36955      */
36956     setTitle : function(title){
36957         this.title = title;
36958         if(this.region){
36959             this.region.updatePanelTitle(this, title);
36960         }
36961     },
36962     
36963     /**
36964      * Returns true is this panel was configured to be closable
36965      * @return {Boolean} 
36966      */
36967     isClosable : function(){
36968         return this.closable;
36969     },
36970     
36971     beforeSlide : function(){
36972         this.el.clip();
36973         this.resizeEl.clip();
36974     },
36975     
36976     afterSlide : function(){
36977         this.el.unclip();
36978         this.resizeEl.unclip();
36979     },
36980     
36981     /**
36982      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36983      *   Will fail silently if the {@link #setUrl} method has not been called.
36984      *   This does not activate the panel, just updates its content.
36985      */
36986     refresh : function(){
36987         if(this.refreshDelegate){
36988            this.loaded = false;
36989            this.refreshDelegate();
36990         }
36991     },
36992     
36993     /**
36994      * Destroys this panel
36995      */
36996     destroy : function(){
36997         this.el.removeAllListeners();
36998         var tempEl = document.createElement("span");
36999         tempEl.appendChild(this.el.dom);
37000         tempEl.innerHTML = "";
37001         this.el.remove();
37002         this.el = null;
37003     },
37004     
37005     /**
37006      * form - if the content panel contains a form - this is a reference to it.
37007      * @type {Roo.form.Form}
37008      */
37009     form : false,
37010     /**
37011      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37012      *    This contains a reference to it.
37013      * @type {Roo.View}
37014      */
37015     view : false,
37016     
37017       /**
37018      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37019      * <pre><code>
37020
37021 layout.addxtype({
37022        xtype : 'Form',
37023        items: [ .... ]
37024    }
37025 );
37026
37027 </code></pre>
37028      * @param {Object} cfg Xtype definition of item to add.
37029      */
37030     
37031     
37032     getChildContainer: function () {
37033         return this.getEl();
37034     }
37035     
37036     
37037     /*
37038         var  ret = new Roo.factory(cfg);
37039         return ret;
37040         
37041         
37042         // add form..
37043         if (cfg.xtype.match(/^Form$/)) {
37044             
37045             var el;
37046             //if (this.footer) {
37047             //    el = this.footer.container.insertSibling(false, 'before');
37048             //} else {
37049                 el = this.el.createChild();
37050             //}
37051
37052             this.form = new  Roo.form.Form(cfg);
37053             
37054             
37055             if ( this.form.allItems.length) {
37056                 this.form.render(el.dom);
37057             }
37058             return this.form;
37059         }
37060         // should only have one of theses..
37061         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37062             // views.. should not be just added - used named prop 'view''
37063             
37064             cfg.el = this.el.appendChild(document.createElement("div"));
37065             // factory?
37066             
37067             var ret = new Roo.factory(cfg);
37068              
37069              ret.render && ret.render(false, ''); // render blank..
37070             this.view = ret;
37071             return ret;
37072         }
37073         return false;
37074     }
37075     \*/
37076 });
37077  
37078 /**
37079  * @class Roo.bootstrap.panel.Grid
37080  * @extends Roo.bootstrap.panel.Content
37081  * @constructor
37082  * Create a new GridPanel.
37083  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37084  * @param {Object} config A the config object
37085   
37086  */
37087
37088
37089
37090 Roo.bootstrap.panel.Grid = function(config)
37091 {
37092     
37093       
37094     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37095         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37096
37097     config.el = this.wrapper;
37098     //this.el = this.wrapper;
37099     
37100       if (config.container) {
37101         // ctor'ed from a Border/panel.grid
37102         
37103         
37104         this.wrapper.setStyle("overflow", "hidden");
37105         this.wrapper.addClass('roo-grid-container');
37106
37107     }
37108     
37109     
37110     if(config.toolbar){
37111         var tool_el = this.wrapper.createChild();    
37112         this.toolbar = Roo.factory(config.toolbar);
37113         var ti = [];
37114         if (config.toolbar.items) {
37115             ti = config.toolbar.items ;
37116             delete config.toolbar.items ;
37117         }
37118         
37119         var nitems = [];
37120         this.toolbar.render(tool_el);
37121         for(var i =0;i < ti.length;i++) {
37122           //  Roo.log(['add child', items[i]]);
37123             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37124         }
37125         this.toolbar.items = nitems;
37126         
37127         delete config.toolbar;
37128     }
37129     
37130     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37131     config.grid.scrollBody = true;;
37132     config.grid.monitorWindowResize = false; // turn off autosizing
37133     config.grid.autoHeight = false;
37134     config.grid.autoWidth = false;
37135     
37136     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37137     
37138     if (config.background) {
37139         // render grid on panel activation (if panel background)
37140         this.on('activate', function(gp) {
37141             if (!gp.grid.rendered) {
37142                 gp.grid.render(this.wrapper);
37143                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37144             }
37145         });
37146             
37147     } else {
37148         this.grid.render(this.wrapper);
37149         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37150
37151     }
37152     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37153     // ??? needed ??? config.el = this.wrapper;
37154     
37155     
37156     
37157   
37158     // xtype created footer. - not sure if will work as we normally have to render first..
37159     if (this.footer && !this.footer.el && this.footer.xtype) {
37160         
37161         var ctr = this.grid.getView().getFooterPanel(true);
37162         this.footer.dataSource = this.grid.dataSource;
37163         this.footer = Roo.factory(this.footer, Roo);
37164         this.footer.render(ctr);
37165         
37166     }
37167     
37168     
37169     
37170     
37171      
37172 };
37173
37174 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37175     getId : function(){
37176         return this.grid.id;
37177     },
37178     
37179     /**
37180      * Returns the grid for this panel
37181      * @return {Roo.bootstrap.Table} 
37182      */
37183     getGrid : function(){
37184         return this.grid;    
37185     },
37186     
37187     setSize : function(width, height){
37188         if(!this.ignoreResize(width, height)){
37189             var grid = this.grid;
37190             var size = this.adjustForComponents(width, height);
37191             var gridel = grid.getGridEl();
37192             gridel.setSize(size.width, size.height);
37193             /*
37194             var thd = grid.getGridEl().select('thead',true).first();
37195             var tbd = grid.getGridEl().select('tbody', true).first();
37196             if (tbd) {
37197                 tbd.setSize(width, height - thd.getHeight());
37198             }
37199             */
37200             grid.autoSize();
37201         }
37202     },
37203      
37204     
37205     
37206     beforeSlide : function(){
37207         this.grid.getView().scroller.clip();
37208     },
37209     
37210     afterSlide : function(){
37211         this.grid.getView().scroller.unclip();
37212     },
37213     
37214     destroy : function(){
37215         this.grid.destroy();
37216         delete this.grid;
37217         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37218     }
37219 });
37220
37221 /**
37222  * @class Roo.bootstrap.panel.Nest
37223  * @extends Roo.bootstrap.panel.Content
37224  * @constructor
37225  * Create a new Panel, that can contain a layout.Border.
37226  * 
37227  * 
37228  * @param {Roo.BorderLayout} layout The layout for this panel
37229  * @param {String/Object} config A string to set only the title or a config object
37230  */
37231 Roo.bootstrap.panel.Nest = function(config)
37232 {
37233     // construct with only one argument..
37234     /* FIXME - implement nicer consturctors
37235     if (layout.layout) {
37236         config = layout;
37237         layout = config.layout;
37238         delete config.layout;
37239     }
37240     if (layout.xtype && !layout.getEl) {
37241         // then layout needs constructing..
37242         layout = Roo.factory(layout, Roo);
37243     }
37244     */
37245     
37246     config.el =  config.layout.getEl();
37247     
37248     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37249     
37250     config.layout.monitorWindowResize = false; // turn off autosizing
37251     this.layout = config.layout;
37252     this.layout.getEl().addClass("roo-layout-nested-layout");
37253     
37254     
37255     
37256     
37257 };
37258
37259 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37260
37261     setSize : function(width, height){
37262         if(!this.ignoreResize(width, height)){
37263             var size = this.adjustForComponents(width, height);
37264             var el = this.layout.getEl();
37265             if (size.height < 1) {
37266                 el.setWidth(size.width);   
37267             } else {
37268                 el.setSize(size.width, size.height);
37269             }
37270             var touch = el.dom.offsetWidth;
37271             this.layout.layout();
37272             // ie requires a double layout on the first pass
37273             if(Roo.isIE && !this.initialized){
37274                 this.initialized = true;
37275                 this.layout.layout();
37276             }
37277         }
37278     },
37279     
37280     // activate all subpanels if not currently active..
37281     
37282     setActiveState : function(active){
37283         this.active = active;
37284         this.setActiveClass(active);
37285         
37286         if(!active){
37287             this.fireEvent("deactivate", this);
37288             return;
37289         }
37290         
37291         this.fireEvent("activate", this);
37292         // not sure if this should happen before or after..
37293         if (!this.layout) {
37294             return; // should not happen..
37295         }
37296         var reg = false;
37297         for (var r in this.layout.regions) {
37298             reg = this.layout.getRegion(r);
37299             if (reg.getActivePanel()) {
37300                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37301                 reg.setActivePanel(reg.getActivePanel());
37302                 continue;
37303             }
37304             if (!reg.panels.length) {
37305                 continue;
37306             }
37307             reg.showPanel(reg.getPanel(0));
37308         }
37309         
37310         
37311         
37312         
37313     },
37314     
37315     /**
37316      * Returns the nested BorderLayout for this panel
37317      * @return {Roo.BorderLayout} 
37318      */
37319     getLayout : function(){
37320         return this.layout;
37321     },
37322     
37323      /**
37324      * Adds a xtype elements to the layout of the nested panel
37325      * <pre><code>
37326
37327 panel.addxtype({
37328        xtype : 'ContentPanel',
37329        region: 'west',
37330        items: [ .... ]
37331    }
37332 );
37333
37334 panel.addxtype({
37335         xtype : 'NestedLayoutPanel',
37336         region: 'west',
37337         layout: {
37338            center: { },
37339            west: { }   
37340         },
37341         items : [ ... list of content panels or nested layout panels.. ]
37342    }
37343 );
37344 </code></pre>
37345      * @param {Object} cfg Xtype definition of item to add.
37346      */
37347     addxtype : function(cfg) {
37348         return this.layout.addxtype(cfg);
37349     
37350     }
37351 });        /*
37352  * Based on:
37353  * Ext JS Library 1.1.1
37354  * Copyright(c) 2006-2007, Ext JS, LLC.
37355  *
37356  * Originally Released Under LGPL - original licence link has changed is not relivant.
37357  *
37358  * Fork - LGPL
37359  * <script type="text/javascript">
37360  */
37361 /**
37362  * @class Roo.TabPanel
37363  * @extends Roo.util.Observable
37364  * A lightweight tab container.
37365  * <br><br>
37366  * Usage:
37367  * <pre><code>
37368 // basic tabs 1, built from existing content
37369 var tabs = new Roo.TabPanel("tabs1");
37370 tabs.addTab("script", "View Script");
37371 tabs.addTab("markup", "View Markup");
37372 tabs.activate("script");
37373
37374 // more advanced tabs, built from javascript
37375 var jtabs = new Roo.TabPanel("jtabs");
37376 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37377
37378 // set up the UpdateManager
37379 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37380 var updater = tab2.getUpdateManager();
37381 updater.setDefaultUrl("ajax1.htm");
37382 tab2.on('activate', updater.refresh, updater, true);
37383
37384 // Use setUrl for Ajax loading
37385 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37386 tab3.setUrl("ajax2.htm", null, true);
37387
37388 // Disabled tab
37389 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37390 tab4.disable();
37391
37392 jtabs.activate("jtabs-1");
37393  * </code></pre>
37394  * @constructor
37395  * Create a new TabPanel.
37396  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37397  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37398  */
37399 Roo.bootstrap.panel.Tabs = function(config){
37400     /**
37401     * The container element for this TabPanel.
37402     * @type Roo.Element
37403     */
37404     this.el = Roo.get(config.el);
37405     delete config.el;
37406     if(config){
37407         if(typeof config == "boolean"){
37408             this.tabPosition = config ? "bottom" : "top";
37409         }else{
37410             Roo.apply(this, config);
37411         }
37412     }
37413     
37414     if(this.tabPosition == "bottom"){
37415         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37416         this.el.addClass("roo-tabs-bottom");
37417     }
37418     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37419     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37420     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37421     if(Roo.isIE){
37422         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37423     }
37424     if(this.tabPosition != "bottom"){
37425         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37426          * @type Roo.Element
37427          */
37428         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37429         this.el.addClass("roo-tabs-top");
37430     }
37431     this.items = [];
37432
37433     this.bodyEl.setStyle("position", "relative");
37434
37435     this.active = null;
37436     this.activateDelegate = this.activate.createDelegate(this);
37437
37438     this.addEvents({
37439         /**
37440          * @event tabchange
37441          * Fires when the active tab changes
37442          * @param {Roo.TabPanel} this
37443          * @param {Roo.TabPanelItem} activePanel The new active tab
37444          */
37445         "tabchange": true,
37446         /**
37447          * @event beforetabchange
37448          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37449          * @param {Roo.TabPanel} this
37450          * @param {Object} e Set cancel to true on this object to cancel the tab change
37451          * @param {Roo.TabPanelItem} tab The tab being changed to
37452          */
37453         "beforetabchange" : true
37454     });
37455
37456     Roo.EventManager.onWindowResize(this.onResize, this);
37457     this.cpad = this.el.getPadding("lr");
37458     this.hiddenCount = 0;
37459
37460
37461     // toolbar on the tabbar support...
37462     if (this.toolbar) {
37463         alert("no toolbar support yet");
37464         this.toolbar  = false;
37465         /*
37466         var tcfg = this.toolbar;
37467         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37468         this.toolbar = new Roo.Toolbar(tcfg);
37469         if (Roo.isSafari) {
37470             var tbl = tcfg.container.child('table', true);
37471             tbl.setAttribute('width', '100%');
37472         }
37473         */
37474         
37475     }
37476    
37477
37478
37479     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37480 };
37481
37482 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37483     /*
37484      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37485      */
37486     tabPosition : "top",
37487     /*
37488      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37489      */
37490     currentTabWidth : 0,
37491     /*
37492      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37493      */
37494     minTabWidth : 40,
37495     /*
37496      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37497      */
37498     maxTabWidth : 250,
37499     /*
37500      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37501      */
37502     preferredTabWidth : 175,
37503     /*
37504      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37505      */
37506     resizeTabs : false,
37507     /*
37508      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37509      */
37510     monitorResize : true,
37511     /*
37512      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37513      */
37514     toolbar : false,
37515
37516     /**
37517      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37518      * @param {String} id The id of the div to use <b>or create</b>
37519      * @param {String} text The text for the tab
37520      * @param {String} content (optional) Content to put in the TabPanelItem body
37521      * @param {Boolean} closable (optional) True to create a close icon on the tab
37522      * @return {Roo.TabPanelItem} The created TabPanelItem
37523      */
37524     addTab : function(id, text, content, closable, tpl)
37525     {
37526         var item = new Roo.bootstrap.panel.TabItem({
37527             panel: this,
37528             id : id,
37529             text : text,
37530             closable : closable,
37531             tpl : tpl
37532         });
37533         this.addTabItem(item);
37534         if(content){
37535             item.setContent(content);
37536         }
37537         return item;
37538     },
37539
37540     /**
37541      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37542      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37543      * @return {Roo.TabPanelItem}
37544      */
37545     getTab : function(id){
37546         return this.items[id];
37547     },
37548
37549     /**
37550      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37551      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37552      */
37553     hideTab : function(id){
37554         var t = this.items[id];
37555         if(!t.isHidden()){
37556            t.setHidden(true);
37557            this.hiddenCount++;
37558            this.autoSizeTabs();
37559         }
37560     },
37561
37562     /**
37563      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37564      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37565      */
37566     unhideTab : function(id){
37567         var t = this.items[id];
37568         if(t.isHidden()){
37569            t.setHidden(false);
37570            this.hiddenCount--;
37571            this.autoSizeTabs();
37572         }
37573     },
37574
37575     /**
37576      * Adds an existing {@link Roo.TabPanelItem}.
37577      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37578      */
37579     addTabItem : function(item){
37580         this.items[item.id] = item;
37581         this.items.push(item);
37582       //  if(this.resizeTabs){
37583     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37584   //         this.autoSizeTabs();
37585 //        }else{
37586 //            item.autoSize();
37587        // }
37588     },
37589
37590     /**
37591      * Removes a {@link Roo.TabPanelItem}.
37592      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37593      */
37594     removeTab : function(id){
37595         var items = this.items;
37596         var tab = items[id];
37597         if(!tab) { return; }
37598         var index = items.indexOf(tab);
37599         if(this.active == tab && items.length > 1){
37600             var newTab = this.getNextAvailable(index);
37601             if(newTab) {
37602                 newTab.activate();
37603             }
37604         }
37605         this.stripEl.dom.removeChild(tab.pnode.dom);
37606         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37607             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37608         }
37609         items.splice(index, 1);
37610         delete this.items[tab.id];
37611         tab.fireEvent("close", tab);
37612         tab.purgeListeners();
37613         this.autoSizeTabs();
37614     },
37615
37616     getNextAvailable : function(start){
37617         var items = this.items;
37618         var index = start;
37619         // look for a next tab that will slide over to
37620         // replace the one being removed
37621         while(index < items.length){
37622             var item = items[++index];
37623             if(item && !item.isHidden()){
37624                 return item;
37625             }
37626         }
37627         // if one isn't found select the previous tab (on the left)
37628         index = start;
37629         while(index >= 0){
37630             var item = items[--index];
37631             if(item && !item.isHidden()){
37632                 return item;
37633             }
37634         }
37635         return null;
37636     },
37637
37638     /**
37639      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37640      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37641      */
37642     disableTab : function(id){
37643         var tab = this.items[id];
37644         if(tab && this.active != tab){
37645             tab.disable();
37646         }
37647     },
37648
37649     /**
37650      * Enables a {@link Roo.TabPanelItem} that is disabled.
37651      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37652      */
37653     enableTab : function(id){
37654         var tab = this.items[id];
37655         tab.enable();
37656     },
37657
37658     /**
37659      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37660      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37661      * @return {Roo.TabPanelItem} The TabPanelItem.
37662      */
37663     activate : function(id){
37664         var tab = this.items[id];
37665         if(!tab){
37666             return null;
37667         }
37668         if(tab == this.active || tab.disabled){
37669             return tab;
37670         }
37671         var e = {};
37672         this.fireEvent("beforetabchange", this, e, tab);
37673         if(e.cancel !== true && !tab.disabled){
37674             if(this.active){
37675                 this.active.hide();
37676             }
37677             this.active = this.items[id];
37678             this.active.show();
37679             this.fireEvent("tabchange", this, this.active);
37680         }
37681         return tab;
37682     },
37683
37684     /**
37685      * Gets the active {@link Roo.TabPanelItem}.
37686      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37687      */
37688     getActiveTab : function(){
37689         return this.active;
37690     },
37691
37692     /**
37693      * Updates the tab body element to fit the height of the container element
37694      * for overflow scrolling
37695      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37696      */
37697     syncHeight : function(targetHeight){
37698         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37699         var bm = this.bodyEl.getMargins();
37700         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37701         this.bodyEl.setHeight(newHeight);
37702         return newHeight;
37703     },
37704
37705     onResize : function(){
37706         if(this.monitorResize){
37707             this.autoSizeTabs();
37708         }
37709     },
37710
37711     /**
37712      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37713      */
37714     beginUpdate : function(){
37715         this.updating = true;
37716     },
37717
37718     /**
37719      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37720      */
37721     endUpdate : function(){
37722         this.updating = false;
37723         this.autoSizeTabs();
37724     },
37725
37726     /**
37727      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37728      */
37729     autoSizeTabs : function(){
37730         var count = this.items.length;
37731         var vcount = count - this.hiddenCount;
37732         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37733             return;
37734         }
37735         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37736         var availWidth = Math.floor(w / vcount);
37737         var b = this.stripBody;
37738         if(b.getWidth() > w){
37739             var tabs = this.items;
37740             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37741             if(availWidth < this.minTabWidth){
37742                 /*if(!this.sleft){    // incomplete scrolling code
37743                     this.createScrollButtons();
37744                 }
37745                 this.showScroll();
37746                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37747             }
37748         }else{
37749             if(this.currentTabWidth < this.preferredTabWidth){
37750                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37751             }
37752         }
37753     },
37754
37755     /**
37756      * Returns the number of tabs in this TabPanel.
37757      * @return {Number}
37758      */
37759      getCount : function(){
37760          return this.items.length;
37761      },
37762
37763     /**
37764      * Resizes all the tabs to the passed width
37765      * @param {Number} The new width
37766      */
37767     setTabWidth : function(width){
37768         this.currentTabWidth = width;
37769         for(var i = 0, len = this.items.length; i < len; i++) {
37770                 if(!this.items[i].isHidden()) {
37771                 this.items[i].setWidth(width);
37772             }
37773         }
37774     },
37775
37776     /**
37777      * Destroys this TabPanel
37778      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37779      */
37780     destroy : function(removeEl){
37781         Roo.EventManager.removeResizeListener(this.onResize, this);
37782         for(var i = 0, len = this.items.length; i < len; i++){
37783             this.items[i].purgeListeners();
37784         }
37785         if(removeEl === true){
37786             this.el.update("");
37787             this.el.remove();
37788         }
37789     },
37790     
37791     createStrip : function(container)
37792     {
37793         var strip = document.createElement("nav");
37794         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37795         container.appendChild(strip);
37796         return strip;
37797     },
37798     
37799     createStripList : function(strip)
37800     {
37801         // div wrapper for retard IE
37802         // returns the "tr" element.
37803         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37804         //'<div class="x-tabs-strip-wrap">'+
37805           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37806           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37807         return strip.firstChild; //.firstChild.firstChild.firstChild;
37808     },
37809     createBody : function(container)
37810     {
37811         var body = document.createElement("div");
37812         Roo.id(body, "tab-body");
37813         //Roo.fly(body).addClass("x-tabs-body");
37814         Roo.fly(body).addClass("tab-content");
37815         container.appendChild(body);
37816         return body;
37817     },
37818     createItemBody :function(bodyEl, id){
37819         var body = Roo.getDom(id);
37820         if(!body){
37821             body = document.createElement("div");
37822             body.id = id;
37823         }
37824         //Roo.fly(body).addClass("x-tabs-item-body");
37825         Roo.fly(body).addClass("tab-pane");
37826          bodyEl.insertBefore(body, bodyEl.firstChild);
37827         return body;
37828     },
37829     /** @private */
37830     createStripElements :  function(stripEl, text, closable, tpl)
37831     {
37832         var td = document.createElement("li"); // was td..
37833         
37834         
37835         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37836         
37837         
37838         stripEl.appendChild(td);
37839         /*if(closable){
37840             td.className = "x-tabs-closable";
37841             if(!this.closeTpl){
37842                 this.closeTpl = new Roo.Template(
37843                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37844                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37845                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37846                 );
37847             }
37848             var el = this.closeTpl.overwrite(td, {"text": text});
37849             var close = el.getElementsByTagName("div")[0];
37850             var inner = el.getElementsByTagName("em")[0];
37851             return {"el": el, "close": close, "inner": inner};
37852         } else {
37853         */
37854         // not sure what this is..
37855 //            if(!this.tabTpl){
37856                 //this.tabTpl = new Roo.Template(
37857                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37858                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37859                 //);
37860 //                this.tabTpl = new Roo.Template(
37861 //                   '<a href="#">' +
37862 //                   '<span unselectable="on"' +
37863 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37864 //                            ' >{text}</span></a>'
37865 //                );
37866 //                
37867 //            }
37868
37869
37870             var template = tpl || this.tabTpl || false;
37871             
37872             if(!template){
37873                 
37874                 template = new Roo.Template(
37875                    '<a href="#">' +
37876                    '<span unselectable="on"' +
37877                             (this.disableTooltips ? '' : ' title="{text}"') +
37878                             ' >{text}</span></a>'
37879                 );
37880             }
37881             
37882             switch (typeof(template)) {
37883                 case 'object' :
37884                     break;
37885                 case 'string' :
37886                     template = new Roo.Template(template);
37887                     break;
37888                 default :
37889                     break;
37890             }
37891             
37892             var el = template.overwrite(td, {"text": text});
37893             
37894             var inner = el.getElementsByTagName("span")[0];
37895             
37896             return {"el": el, "inner": inner};
37897             
37898     }
37899         
37900     
37901 });
37902
37903 /**
37904  * @class Roo.TabPanelItem
37905  * @extends Roo.util.Observable
37906  * Represents an individual item (tab plus body) in a TabPanel.
37907  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37908  * @param {String} id The id of this TabPanelItem
37909  * @param {String} text The text for the tab of this TabPanelItem
37910  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37911  */
37912 Roo.bootstrap.panel.TabItem = function(config){
37913     /**
37914      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37915      * @type Roo.TabPanel
37916      */
37917     this.tabPanel = config.panel;
37918     /**
37919      * The id for this TabPanelItem
37920      * @type String
37921      */
37922     this.id = config.id;
37923     /** @private */
37924     this.disabled = false;
37925     /** @private */
37926     this.text = config.text;
37927     /** @private */
37928     this.loaded = false;
37929     this.closable = config.closable;
37930
37931     /**
37932      * The body element for this TabPanelItem.
37933      * @type Roo.Element
37934      */
37935     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37936     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37937     this.bodyEl.setStyle("display", "block");
37938     this.bodyEl.setStyle("zoom", "1");
37939     //this.hideAction();
37940
37941     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37942     /** @private */
37943     this.el = Roo.get(els.el);
37944     this.inner = Roo.get(els.inner, true);
37945     this.textEl = Roo.get(this.el.dom.firstChild, true);
37946     this.pnode = Roo.get(els.el.parentNode, true);
37947 //    this.el.on("mousedown", this.onTabMouseDown, this);
37948     this.el.on("click", this.onTabClick, this);
37949     /** @private */
37950     if(config.closable){
37951         var c = Roo.get(els.close, true);
37952         c.dom.title = this.closeText;
37953         c.addClassOnOver("close-over");
37954         c.on("click", this.closeClick, this);
37955      }
37956
37957     this.addEvents({
37958          /**
37959          * @event activate
37960          * Fires when this tab becomes the active tab.
37961          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37962          * @param {Roo.TabPanelItem} this
37963          */
37964         "activate": true,
37965         /**
37966          * @event beforeclose
37967          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37968          * @param {Roo.TabPanelItem} this
37969          * @param {Object} e Set cancel to true on this object to cancel the close.
37970          */
37971         "beforeclose": true,
37972         /**
37973          * @event close
37974          * Fires when this tab is closed.
37975          * @param {Roo.TabPanelItem} this
37976          */
37977          "close": true,
37978         /**
37979          * @event deactivate
37980          * Fires when this tab is no longer the active tab.
37981          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37982          * @param {Roo.TabPanelItem} this
37983          */
37984          "deactivate" : true
37985     });
37986     this.hidden = false;
37987
37988     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37989 };
37990
37991 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37992            {
37993     purgeListeners : function(){
37994        Roo.util.Observable.prototype.purgeListeners.call(this);
37995        this.el.removeAllListeners();
37996     },
37997     /**
37998      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37999      */
38000     show : function(){
38001         this.pnode.addClass("active");
38002         this.showAction();
38003         if(Roo.isOpera){
38004             this.tabPanel.stripWrap.repaint();
38005         }
38006         this.fireEvent("activate", this.tabPanel, this);
38007     },
38008
38009     /**
38010      * Returns true if this tab is the active tab.
38011      * @return {Boolean}
38012      */
38013     isActive : function(){
38014         return this.tabPanel.getActiveTab() == this;
38015     },
38016
38017     /**
38018      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38019      */
38020     hide : function(){
38021         this.pnode.removeClass("active");
38022         this.hideAction();
38023         this.fireEvent("deactivate", this.tabPanel, this);
38024     },
38025
38026     hideAction : function(){
38027         this.bodyEl.hide();
38028         this.bodyEl.setStyle("position", "absolute");
38029         this.bodyEl.setLeft("-20000px");
38030         this.bodyEl.setTop("-20000px");
38031     },
38032
38033     showAction : function(){
38034         this.bodyEl.setStyle("position", "relative");
38035         this.bodyEl.setTop("");
38036         this.bodyEl.setLeft("");
38037         this.bodyEl.show();
38038     },
38039
38040     /**
38041      * Set the tooltip for the tab.
38042      * @param {String} tooltip The tab's tooltip
38043      */
38044     setTooltip : function(text){
38045         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38046             this.textEl.dom.qtip = text;
38047             this.textEl.dom.removeAttribute('title');
38048         }else{
38049             this.textEl.dom.title = text;
38050         }
38051     },
38052
38053     onTabClick : function(e){
38054         e.preventDefault();
38055         this.tabPanel.activate(this.id);
38056     },
38057
38058     onTabMouseDown : function(e){
38059         e.preventDefault();
38060         this.tabPanel.activate(this.id);
38061     },
38062 /*
38063     getWidth : function(){
38064         return this.inner.getWidth();
38065     },
38066
38067     setWidth : function(width){
38068         var iwidth = width - this.pnode.getPadding("lr");
38069         this.inner.setWidth(iwidth);
38070         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38071         this.pnode.setWidth(width);
38072     },
38073 */
38074     /**
38075      * Show or hide the tab
38076      * @param {Boolean} hidden True to hide or false to show.
38077      */
38078     setHidden : function(hidden){
38079         this.hidden = hidden;
38080         this.pnode.setStyle("display", hidden ? "none" : "");
38081     },
38082
38083     /**
38084      * Returns true if this tab is "hidden"
38085      * @return {Boolean}
38086      */
38087     isHidden : function(){
38088         return this.hidden;
38089     },
38090
38091     /**
38092      * Returns the text for this tab
38093      * @return {String}
38094      */
38095     getText : function(){
38096         return this.text;
38097     },
38098     /*
38099     autoSize : function(){
38100         //this.el.beginMeasure();
38101         this.textEl.setWidth(1);
38102         /*
38103          *  #2804 [new] Tabs in Roojs
38104          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38105          */
38106         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38107         //this.el.endMeasure();
38108     //},
38109
38110     /**
38111      * Sets the text for the tab (Note: this also sets the tooltip text)
38112      * @param {String} text The tab's text and tooltip
38113      */
38114     setText : function(text){
38115         this.text = text;
38116         this.textEl.update(text);
38117         this.setTooltip(text);
38118         //if(!this.tabPanel.resizeTabs){
38119         //    this.autoSize();
38120         //}
38121     },
38122     /**
38123      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38124      */
38125     activate : function(){
38126         this.tabPanel.activate(this.id);
38127     },
38128
38129     /**
38130      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38131      */
38132     disable : function(){
38133         if(this.tabPanel.active != this){
38134             this.disabled = true;
38135             this.pnode.addClass("disabled");
38136         }
38137     },
38138
38139     /**
38140      * Enables this TabPanelItem if it was previously disabled.
38141      */
38142     enable : function(){
38143         this.disabled = false;
38144         this.pnode.removeClass("disabled");
38145     },
38146
38147     /**
38148      * Sets the content for this TabPanelItem.
38149      * @param {String} content The content
38150      * @param {Boolean} loadScripts true to look for and load scripts
38151      */
38152     setContent : function(content, loadScripts){
38153         this.bodyEl.update(content, loadScripts);
38154     },
38155
38156     /**
38157      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38158      * @return {Roo.UpdateManager} The UpdateManager
38159      */
38160     getUpdateManager : function(){
38161         return this.bodyEl.getUpdateManager();
38162     },
38163
38164     /**
38165      * Set a URL to be used to load the content for this TabPanelItem.
38166      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38167      * @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)
38168      * @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)
38169      * @return {Roo.UpdateManager} The UpdateManager
38170      */
38171     setUrl : function(url, params, loadOnce){
38172         if(this.refreshDelegate){
38173             this.un('activate', this.refreshDelegate);
38174         }
38175         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38176         this.on("activate", this.refreshDelegate);
38177         return this.bodyEl.getUpdateManager();
38178     },
38179
38180     /** @private */
38181     _handleRefresh : function(url, params, loadOnce){
38182         if(!loadOnce || !this.loaded){
38183             var updater = this.bodyEl.getUpdateManager();
38184             updater.update(url, params, this._setLoaded.createDelegate(this));
38185         }
38186     },
38187
38188     /**
38189      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38190      *   Will fail silently if the setUrl method has not been called.
38191      *   This does not activate the panel, just updates its content.
38192      */
38193     refresh : function(){
38194         if(this.refreshDelegate){
38195            this.loaded = false;
38196            this.refreshDelegate();
38197         }
38198     },
38199
38200     /** @private */
38201     _setLoaded : function(){
38202         this.loaded = true;
38203     },
38204
38205     /** @private */
38206     closeClick : function(e){
38207         var o = {};
38208         e.stopEvent();
38209         this.fireEvent("beforeclose", this, o);
38210         if(o.cancel !== true){
38211             this.tabPanel.removeTab(this.id);
38212         }
38213     },
38214     /**
38215      * The text displayed in the tooltip for the close icon.
38216      * @type String
38217      */
38218     closeText : "Close this tab"
38219 });
38220 /**
38221 *    This script refer to:
38222 *    Title: International Telephone Input
38223 *    Author: Jack O'Connor
38224 *    Code version:  v12.1.12
38225 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38226 **/
38227
38228 Roo.bootstrap.PhoneInputData = function() {
38229     var d = [
38230       [
38231         "Afghanistan (‫افغانستان‬‎)",
38232         "af",
38233         "93"
38234       ],
38235       [
38236         "Albania (Shqipëri)",
38237         "al",
38238         "355"
38239       ],
38240       [
38241         "Algeria (‫الجزائر‬‎)",
38242         "dz",
38243         "213"
38244       ],
38245       [
38246         "American Samoa",
38247         "as",
38248         "1684"
38249       ],
38250       [
38251         "Andorra",
38252         "ad",
38253         "376"
38254       ],
38255       [
38256         "Angola",
38257         "ao",
38258         "244"
38259       ],
38260       [
38261         "Anguilla",
38262         "ai",
38263         "1264"
38264       ],
38265       [
38266         "Antigua and Barbuda",
38267         "ag",
38268         "1268"
38269       ],
38270       [
38271         "Argentina",
38272         "ar",
38273         "54"
38274       ],
38275       [
38276         "Armenia (Հայաստան)",
38277         "am",
38278         "374"
38279       ],
38280       [
38281         "Aruba",
38282         "aw",
38283         "297"
38284       ],
38285       [
38286         "Australia",
38287         "au",
38288         "61",
38289         0
38290       ],
38291       [
38292         "Austria (Österreich)",
38293         "at",
38294         "43"
38295       ],
38296       [
38297         "Azerbaijan (Azərbaycan)",
38298         "az",
38299         "994"
38300       ],
38301       [
38302         "Bahamas",
38303         "bs",
38304         "1242"
38305       ],
38306       [
38307         "Bahrain (‫البحرين‬‎)",
38308         "bh",
38309         "973"
38310       ],
38311       [
38312         "Bangladesh (বাংলাদেশ)",
38313         "bd",
38314         "880"
38315       ],
38316       [
38317         "Barbados",
38318         "bb",
38319         "1246"
38320       ],
38321       [
38322         "Belarus (Беларусь)",
38323         "by",
38324         "375"
38325       ],
38326       [
38327         "Belgium (België)",
38328         "be",
38329         "32"
38330       ],
38331       [
38332         "Belize",
38333         "bz",
38334         "501"
38335       ],
38336       [
38337         "Benin (Bénin)",
38338         "bj",
38339         "229"
38340       ],
38341       [
38342         "Bermuda",
38343         "bm",
38344         "1441"
38345       ],
38346       [
38347         "Bhutan (འབྲུག)",
38348         "bt",
38349         "975"
38350       ],
38351       [
38352         "Bolivia",
38353         "bo",
38354         "591"
38355       ],
38356       [
38357         "Bosnia and Herzegovina (Босна и Херцеговина)",
38358         "ba",
38359         "387"
38360       ],
38361       [
38362         "Botswana",
38363         "bw",
38364         "267"
38365       ],
38366       [
38367         "Brazil (Brasil)",
38368         "br",
38369         "55"
38370       ],
38371       [
38372         "British Indian Ocean Territory",
38373         "io",
38374         "246"
38375       ],
38376       [
38377         "British Virgin Islands",
38378         "vg",
38379         "1284"
38380       ],
38381       [
38382         "Brunei",
38383         "bn",
38384         "673"
38385       ],
38386       [
38387         "Bulgaria (България)",
38388         "bg",
38389         "359"
38390       ],
38391       [
38392         "Burkina Faso",
38393         "bf",
38394         "226"
38395       ],
38396       [
38397         "Burundi (Uburundi)",
38398         "bi",
38399         "257"
38400       ],
38401       [
38402         "Cambodia (កម្ពុជា)",
38403         "kh",
38404         "855"
38405       ],
38406       [
38407         "Cameroon (Cameroun)",
38408         "cm",
38409         "237"
38410       ],
38411       [
38412         "Canada",
38413         "ca",
38414         "1",
38415         1,
38416         ["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"]
38417       ],
38418       [
38419         "Cape Verde (Kabu Verdi)",
38420         "cv",
38421         "238"
38422       ],
38423       [
38424         "Caribbean Netherlands",
38425         "bq",
38426         "599",
38427         1
38428       ],
38429       [
38430         "Cayman Islands",
38431         "ky",
38432         "1345"
38433       ],
38434       [
38435         "Central African Republic (République centrafricaine)",
38436         "cf",
38437         "236"
38438       ],
38439       [
38440         "Chad (Tchad)",
38441         "td",
38442         "235"
38443       ],
38444       [
38445         "Chile",
38446         "cl",
38447         "56"
38448       ],
38449       [
38450         "China (中国)",
38451         "cn",
38452         "86"
38453       ],
38454       [
38455         "Christmas Island",
38456         "cx",
38457         "61",
38458         2
38459       ],
38460       [
38461         "Cocos (Keeling) Islands",
38462         "cc",
38463         "61",
38464         1
38465       ],
38466       [
38467         "Colombia",
38468         "co",
38469         "57"
38470       ],
38471       [
38472         "Comoros (‫جزر القمر‬‎)",
38473         "km",
38474         "269"
38475       ],
38476       [
38477         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38478         "cd",
38479         "243"
38480       ],
38481       [
38482         "Congo (Republic) (Congo-Brazzaville)",
38483         "cg",
38484         "242"
38485       ],
38486       [
38487         "Cook Islands",
38488         "ck",
38489         "682"
38490       ],
38491       [
38492         "Costa Rica",
38493         "cr",
38494         "506"
38495       ],
38496       [
38497         "Côte d’Ivoire",
38498         "ci",
38499         "225"
38500       ],
38501       [
38502         "Croatia (Hrvatska)",
38503         "hr",
38504         "385"
38505       ],
38506       [
38507         "Cuba",
38508         "cu",
38509         "53"
38510       ],
38511       [
38512         "Curaçao",
38513         "cw",
38514         "599",
38515         0
38516       ],
38517       [
38518         "Cyprus (Κύπρος)",
38519         "cy",
38520         "357"
38521       ],
38522       [
38523         "Czech Republic (Česká republika)",
38524         "cz",
38525         "420"
38526       ],
38527       [
38528         "Denmark (Danmark)",
38529         "dk",
38530         "45"
38531       ],
38532       [
38533         "Djibouti",
38534         "dj",
38535         "253"
38536       ],
38537       [
38538         "Dominica",
38539         "dm",
38540         "1767"
38541       ],
38542       [
38543         "Dominican Republic (República Dominicana)",
38544         "do",
38545         "1",
38546         2,
38547         ["809", "829", "849"]
38548       ],
38549       [
38550         "Ecuador",
38551         "ec",
38552         "593"
38553       ],
38554       [
38555         "Egypt (‫مصر‬‎)",
38556         "eg",
38557         "20"
38558       ],
38559       [
38560         "El Salvador",
38561         "sv",
38562         "503"
38563       ],
38564       [
38565         "Equatorial Guinea (Guinea Ecuatorial)",
38566         "gq",
38567         "240"
38568       ],
38569       [
38570         "Eritrea",
38571         "er",
38572         "291"
38573       ],
38574       [
38575         "Estonia (Eesti)",
38576         "ee",
38577         "372"
38578       ],
38579       [
38580         "Ethiopia",
38581         "et",
38582         "251"
38583       ],
38584       [
38585         "Falkland Islands (Islas Malvinas)",
38586         "fk",
38587         "500"
38588       ],
38589       [
38590         "Faroe Islands (Føroyar)",
38591         "fo",
38592         "298"
38593       ],
38594       [
38595         "Fiji",
38596         "fj",
38597         "679"
38598       ],
38599       [
38600         "Finland (Suomi)",
38601         "fi",
38602         "358",
38603         0
38604       ],
38605       [
38606         "France",
38607         "fr",
38608         "33"
38609       ],
38610       [
38611         "French Guiana (Guyane française)",
38612         "gf",
38613         "594"
38614       ],
38615       [
38616         "French Polynesia (Polynésie française)",
38617         "pf",
38618         "689"
38619       ],
38620       [
38621         "Gabon",
38622         "ga",
38623         "241"
38624       ],
38625       [
38626         "Gambia",
38627         "gm",
38628         "220"
38629       ],
38630       [
38631         "Georgia (საქართველო)",
38632         "ge",
38633         "995"
38634       ],
38635       [
38636         "Germany (Deutschland)",
38637         "de",
38638         "49"
38639       ],
38640       [
38641         "Ghana (Gaana)",
38642         "gh",
38643         "233"
38644       ],
38645       [
38646         "Gibraltar",
38647         "gi",
38648         "350"
38649       ],
38650       [
38651         "Greece (Ελλάδα)",
38652         "gr",
38653         "30"
38654       ],
38655       [
38656         "Greenland (Kalaallit Nunaat)",
38657         "gl",
38658         "299"
38659       ],
38660       [
38661         "Grenada",
38662         "gd",
38663         "1473"
38664       ],
38665       [
38666         "Guadeloupe",
38667         "gp",
38668         "590",
38669         0
38670       ],
38671       [
38672         "Guam",
38673         "gu",
38674         "1671"
38675       ],
38676       [
38677         "Guatemala",
38678         "gt",
38679         "502"
38680       ],
38681       [
38682         "Guernsey",
38683         "gg",
38684         "44",
38685         1
38686       ],
38687       [
38688         "Guinea (Guinée)",
38689         "gn",
38690         "224"
38691       ],
38692       [
38693         "Guinea-Bissau (Guiné Bissau)",
38694         "gw",
38695         "245"
38696       ],
38697       [
38698         "Guyana",
38699         "gy",
38700         "592"
38701       ],
38702       [
38703         "Haiti",
38704         "ht",
38705         "509"
38706       ],
38707       [
38708         "Honduras",
38709         "hn",
38710         "504"
38711       ],
38712       [
38713         "Hong Kong (香港)",
38714         "hk",
38715         "852"
38716       ],
38717       [
38718         "Hungary (Magyarország)",
38719         "hu",
38720         "36"
38721       ],
38722       [
38723         "Iceland (Ísland)",
38724         "is",
38725         "354"
38726       ],
38727       [
38728         "India (भारत)",
38729         "in",
38730         "91"
38731       ],
38732       [
38733         "Indonesia",
38734         "id",
38735         "62"
38736       ],
38737       [
38738         "Iran (‫ایران‬‎)",
38739         "ir",
38740         "98"
38741       ],
38742       [
38743         "Iraq (‫العراق‬‎)",
38744         "iq",
38745         "964"
38746       ],
38747       [
38748         "Ireland",
38749         "ie",
38750         "353"
38751       ],
38752       [
38753         "Isle of Man",
38754         "im",
38755         "44",
38756         2
38757       ],
38758       [
38759         "Israel (‫ישראל‬‎)",
38760         "il",
38761         "972"
38762       ],
38763       [
38764         "Italy (Italia)",
38765         "it",
38766         "39",
38767         0
38768       ],
38769       [
38770         "Jamaica",
38771         "jm",
38772         "1876"
38773       ],
38774       [
38775         "Japan (日本)",
38776         "jp",
38777         "81"
38778       ],
38779       [
38780         "Jersey",
38781         "je",
38782         "44",
38783         3
38784       ],
38785       [
38786         "Jordan (‫الأردن‬‎)",
38787         "jo",
38788         "962"
38789       ],
38790       [
38791         "Kazakhstan (Казахстан)",
38792         "kz",
38793         "7",
38794         1
38795       ],
38796       [
38797         "Kenya",
38798         "ke",
38799         "254"
38800       ],
38801       [
38802         "Kiribati",
38803         "ki",
38804         "686"
38805       ],
38806       [
38807         "Kosovo",
38808         "xk",
38809         "383"
38810       ],
38811       [
38812         "Kuwait (‫الكويت‬‎)",
38813         "kw",
38814         "965"
38815       ],
38816       [
38817         "Kyrgyzstan (Кыргызстан)",
38818         "kg",
38819         "996"
38820       ],
38821       [
38822         "Laos (ລາວ)",
38823         "la",
38824         "856"
38825       ],
38826       [
38827         "Latvia (Latvija)",
38828         "lv",
38829         "371"
38830       ],
38831       [
38832         "Lebanon (‫لبنان‬‎)",
38833         "lb",
38834         "961"
38835       ],
38836       [
38837         "Lesotho",
38838         "ls",
38839         "266"
38840       ],
38841       [
38842         "Liberia",
38843         "lr",
38844         "231"
38845       ],
38846       [
38847         "Libya (‫ليبيا‬‎)",
38848         "ly",
38849         "218"
38850       ],
38851       [
38852         "Liechtenstein",
38853         "li",
38854         "423"
38855       ],
38856       [
38857         "Lithuania (Lietuva)",
38858         "lt",
38859         "370"
38860       ],
38861       [
38862         "Luxembourg",
38863         "lu",
38864         "352"
38865       ],
38866       [
38867         "Macau (澳門)",
38868         "mo",
38869         "853"
38870       ],
38871       [
38872         "Macedonia (FYROM) (Македонија)",
38873         "mk",
38874         "389"
38875       ],
38876       [
38877         "Madagascar (Madagasikara)",
38878         "mg",
38879         "261"
38880       ],
38881       [
38882         "Malawi",
38883         "mw",
38884         "265"
38885       ],
38886       [
38887         "Malaysia",
38888         "my",
38889         "60"
38890       ],
38891       [
38892         "Maldives",
38893         "mv",
38894         "960"
38895       ],
38896       [
38897         "Mali",
38898         "ml",
38899         "223"
38900       ],
38901       [
38902         "Malta",
38903         "mt",
38904         "356"
38905       ],
38906       [
38907         "Marshall Islands",
38908         "mh",
38909         "692"
38910       ],
38911       [
38912         "Martinique",
38913         "mq",
38914         "596"
38915       ],
38916       [
38917         "Mauritania (‫موريتانيا‬‎)",
38918         "mr",
38919         "222"
38920       ],
38921       [
38922         "Mauritius (Moris)",
38923         "mu",
38924         "230"
38925       ],
38926       [
38927         "Mayotte",
38928         "yt",
38929         "262",
38930         1
38931       ],
38932       [
38933         "Mexico (México)",
38934         "mx",
38935         "52"
38936       ],
38937       [
38938         "Micronesia",
38939         "fm",
38940         "691"
38941       ],
38942       [
38943         "Moldova (Republica Moldova)",
38944         "md",
38945         "373"
38946       ],
38947       [
38948         "Monaco",
38949         "mc",
38950         "377"
38951       ],
38952       [
38953         "Mongolia (Монгол)",
38954         "mn",
38955         "976"
38956       ],
38957       [
38958         "Montenegro (Crna Gora)",
38959         "me",
38960         "382"
38961       ],
38962       [
38963         "Montserrat",
38964         "ms",
38965         "1664"
38966       ],
38967       [
38968         "Morocco (‫المغرب‬‎)",
38969         "ma",
38970         "212",
38971         0
38972       ],
38973       [
38974         "Mozambique (Moçambique)",
38975         "mz",
38976         "258"
38977       ],
38978       [
38979         "Myanmar (Burma) (မြန်မာ)",
38980         "mm",
38981         "95"
38982       ],
38983       [
38984         "Namibia (Namibië)",
38985         "na",
38986         "264"
38987       ],
38988       [
38989         "Nauru",
38990         "nr",
38991         "674"
38992       ],
38993       [
38994         "Nepal (नेपाल)",
38995         "np",
38996         "977"
38997       ],
38998       [
38999         "Netherlands (Nederland)",
39000         "nl",
39001         "31"
39002       ],
39003       [
39004         "New Caledonia (Nouvelle-Calédonie)",
39005         "nc",
39006         "687"
39007       ],
39008       [
39009         "New Zealand",
39010         "nz",
39011         "64"
39012       ],
39013       [
39014         "Nicaragua",
39015         "ni",
39016         "505"
39017       ],
39018       [
39019         "Niger (Nijar)",
39020         "ne",
39021         "227"
39022       ],
39023       [
39024         "Nigeria",
39025         "ng",
39026         "234"
39027       ],
39028       [
39029         "Niue",
39030         "nu",
39031         "683"
39032       ],
39033       [
39034         "Norfolk Island",
39035         "nf",
39036         "672"
39037       ],
39038       [
39039         "North Korea (조선 민주주의 인민 공화국)",
39040         "kp",
39041         "850"
39042       ],
39043       [
39044         "Northern Mariana Islands",
39045         "mp",
39046         "1670"
39047       ],
39048       [
39049         "Norway (Norge)",
39050         "no",
39051         "47",
39052         0
39053       ],
39054       [
39055         "Oman (‫عُمان‬‎)",
39056         "om",
39057         "968"
39058       ],
39059       [
39060         "Pakistan (‫پاکستان‬‎)",
39061         "pk",
39062         "92"
39063       ],
39064       [
39065         "Palau",
39066         "pw",
39067         "680"
39068       ],
39069       [
39070         "Palestine (‫فلسطين‬‎)",
39071         "ps",
39072         "970"
39073       ],
39074       [
39075         "Panama (Panamá)",
39076         "pa",
39077         "507"
39078       ],
39079       [
39080         "Papua New Guinea",
39081         "pg",
39082         "675"
39083       ],
39084       [
39085         "Paraguay",
39086         "py",
39087         "595"
39088       ],
39089       [
39090         "Peru (Perú)",
39091         "pe",
39092         "51"
39093       ],
39094       [
39095         "Philippines",
39096         "ph",
39097         "63"
39098       ],
39099       [
39100         "Poland (Polska)",
39101         "pl",
39102         "48"
39103       ],
39104       [
39105         "Portugal",
39106         "pt",
39107         "351"
39108       ],
39109       [
39110         "Puerto Rico",
39111         "pr",
39112         "1",
39113         3,
39114         ["787", "939"]
39115       ],
39116       [
39117         "Qatar (‫قطر‬‎)",
39118         "qa",
39119         "974"
39120       ],
39121       [
39122         "Réunion (La Réunion)",
39123         "re",
39124         "262",
39125         0
39126       ],
39127       [
39128         "Romania (România)",
39129         "ro",
39130         "40"
39131       ],
39132       [
39133         "Russia (Россия)",
39134         "ru",
39135         "7",
39136         0
39137       ],
39138       [
39139         "Rwanda",
39140         "rw",
39141         "250"
39142       ],
39143       [
39144         "Saint Barthélemy",
39145         "bl",
39146         "590",
39147         1
39148       ],
39149       [
39150         "Saint Helena",
39151         "sh",
39152         "290"
39153       ],
39154       [
39155         "Saint Kitts and Nevis",
39156         "kn",
39157         "1869"
39158       ],
39159       [
39160         "Saint Lucia",
39161         "lc",
39162         "1758"
39163       ],
39164       [
39165         "Saint Martin (Saint-Martin (partie française))",
39166         "mf",
39167         "590",
39168         2
39169       ],
39170       [
39171         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39172         "pm",
39173         "508"
39174       ],
39175       [
39176         "Saint Vincent and the Grenadines",
39177         "vc",
39178         "1784"
39179       ],
39180       [
39181         "Samoa",
39182         "ws",
39183         "685"
39184       ],
39185       [
39186         "San Marino",
39187         "sm",
39188         "378"
39189       ],
39190       [
39191         "São Tomé and Príncipe (São Tomé e Príncipe)",
39192         "st",
39193         "239"
39194       ],
39195       [
39196         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39197         "sa",
39198         "966"
39199       ],
39200       [
39201         "Senegal (Sénégal)",
39202         "sn",
39203         "221"
39204       ],
39205       [
39206         "Serbia (Србија)",
39207         "rs",
39208         "381"
39209       ],
39210       [
39211         "Seychelles",
39212         "sc",
39213         "248"
39214       ],
39215       [
39216         "Sierra Leone",
39217         "sl",
39218         "232"
39219       ],
39220       [
39221         "Singapore",
39222         "sg",
39223         "65"
39224       ],
39225       [
39226         "Sint Maarten",
39227         "sx",
39228         "1721"
39229       ],
39230       [
39231         "Slovakia (Slovensko)",
39232         "sk",
39233         "421"
39234       ],
39235       [
39236         "Slovenia (Slovenija)",
39237         "si",
39238         "386"
39239       ],
39240       [
39241         "Solomon Islands",
39242         "sb",
39243         "677"
39244       ],
39245       [
39246         "Somalia (Soomaaliya)",
39247         "so",
39248         "252"
39249       ],
39250       [
39251         "South Africa",
39252         "za",
39253         "27"
39254       ],
39255       [
39256         "South Korea (대한민국)",
39257         "kr",
39258         "82"
39259       ],
39260       [
39261         "South Sudan (‫جنوب السودان‬‎)",
39262         "ss",
39263         "211"
39264       ],
39265       [
39266         "Spain (España)",
39267         "es",
39268         "34"
39269       ],
39270       [
39271         "Sri Lanka (ශ්‍රී ලංකාව)",
39272         "lk",
39273         "94"
39274       ],
39275       [
39276         "Sudan (‫السودان‬‎)",
39277         "sd",
39278         "249"
39279       ],
39280       [
39281         "Suriname",
39282         "sr",
39283         "597"
39284       ],
39285       [
39286         "Svalbard and Jan Mayen",
39287         "sj",
39288         "47",
39289         1
39290       ],
39291       [
39292         "Swaziland",
39293         "sz",
39294         "268"
39295       ],
39296       [
39297         "Sweden (Sverige)",
39298         "se",
39299         "46"
39300       ],
39301       [
39302         "Switzerland (Schweiz)",
39303         "ch",
39304         "41"
39305       ],
39306       [
39307         "Syria (‫سوريا‬‎)",
39308         "sy",
39309         "963"
39310       ],
39311       [
39312         "Taiwan (台灣)",
39313         "tw",
39314         "886"
39315       ],
39316       [
39317         "Tajikistan",
39318         "tj",
39319         "992"
39320       ],
39321       [
39322         "Tanzania",
39323         "tz",
39324         "255"
39325       ],
39326       [
39327         "Thailand (ไทย)",
39328         "th",
39329         "66"
39330       ],
39331       [
39332         "Timor-Leste",
39333         "tl",
39334         "670"
39335       ],
39336       [
39337         "Togo",
39338         "tg",
39339         "228"
39340       ],
39341       [
39342         "Tokelau",
39343         "tk",
39344         "690"
39345       ],
39346       [
39347         "Tonga",
39348         "to",
39349         "676"
39350       ],
39351       [
39352         "Trinidad and Tobago",
39353         "tt",
39354         "1868"
39355       ],
39356       [
39357         "Tunisia (‫تونس‬‎)",
39358         "tn",
39359         "216"
39360       ],
39361       [
39362         "Turkey (Türkiye)",
39363         "tr",
39364         "90"
39365       ],
39366       [
39367         "Turkmenistan",
39368         "tm",
39369         "993"
39370       ],
39371       [
39372         "Turks and Caicos Islands",
39373         "tc",
39374         "1649"
39375       ],
39376       [
39377         "Tuvalu",
39378         "tv",
39379         "688"
39380       ],
39381       [
39382         "U.S. Virgin Islands",
39383         "vi",
39384         "1340"
39385       ],
39386       [
39387         "Uganda",
39388         "ug",
39389         "256"
39390       ],
39391       [
39392         "Ukraine (Україна)",
39393         "ua",
39394         "380"
39395       ],
39396       [
39397         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39398         "ae",
39399         "971"
39400       ],
39401       [
39402         "United Kingdom",
39403         "gb",
39404         "44",
39405         0
39406       ],
39407       [
39408         "United States",
39409         "us",
39410         "1",
39411         0
39412       ],
39413       [
39414         "Uruguay",
39415         "uy",
39416         "598"
39417       ],
39418       [
39419         "Uzbekistan (Oʻzbekiston)",
39420         "uz",
39421         "998"
39422       ],
39423       [
39424         "Vanuatu",
39425         "vu",
39426         "678"
39427       ],
39428       [
39429         "Vatican City (Città del Vaticano)",
39430         "va",
39431         "39",
39432         1
39433       ],
39434       [
39435         "Venezuela",
39436         "ve",
39437         "58"
39438       ],
39439       [
39440         "Vietnam (Việt Nam)",
39441         "vn",
39442         "84"
39443       ],
39444       [
39445         "Wallis and Futuna (Wallis-et-Futuna)",
39446         "wf",
39447         "681"
39448       ],
39449       [
39450         "Western Sahara (‫الصحراء الغربية‬‎)",
39451         "eh",
39452         "212",
39453         1
39454       ],
39455       [
39456         "Yemen (‫اليمن‬‎)",
39457         "ye",
39458         "967"
39459       ],
39460       [
39461         "Zambia",
39462         "zm",
39463         "260"
39464       ],
39465       [
39466         "Zimbabwe",
39467         "zw",
39468         "263"
39469       ],
39470       [
39471         "Åland Islands",
39472         "ax",
39473         "358",
39474         1
39475       ]
39476   ];
39477   
39478   return d;
39479 }/**
39480 *    This script refer to:
39481 *    Title: International Telephone Input
39482 *    Author: Jack O'Connor
39483 *    Code version:  v12.1.12
39484 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39485 **/
39486
39487 /**
39488  * @class Roo.bootstrap.PhoneInput
39489  * @extends Roo.bootstrap.TriggerField
39490  * An input with International dial-code selection
39491  
39492  * @cfg {String} defaultDialCode default '+852'
39493  * @cfg {Array} preferedCountries default []
39494   
39495  * @constructor
39496  * Create a new PhoneInput.
39497  * @param {Object} config Configuration options
39498  */
39499
39500 Roo.bootstrap.PhoneInput = function(config) {
39501     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39502 };
39503
39504 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39505         
39506         listWidth: undefined,
39507         
39508         selectedClass: 'active',
39509         
39510         invalidClass : "has-warning",
39511         
39512         validClass: 'has-success',
39513         
39514         allowed: '0123456789',
39515         
39516         /**
39517          * @cfg {String} defaultDialCode The default dial code when initializing the input
39518          */
39519         defaultDialCode: '+852',
39520         
39521         /**
39522          * @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
39523          */
39524         preferedCountries: false,
39525         
39526         getAutoCreate : function()
39527         {
39528             var data = Roo.bootstrap.PhoneInputData();
39529             var align = this.labelAlign || this.parentLabelAlign();
39530             var id = Roo.id();
39531             
39532             this.allCountries = [];
39533             this.dialCodeMapping = [];
39534             
39535             for (var i = 0; i < data.length; i++) {
39536               var c = data[i];
39537               this.allCountries[i] = {
39538                 name: c[0],
39539                 iso2: c[1],
39540                 dialCode: c[2],
39541                 priority: c[3] || 0,
39542                 areaCodes: c[4] || null
39543               };
39544               this.dialCodeMapping[c[2]] = {
39545                   name: c[0],
39546                   iso2: c[1],
39547                   priority: c[3] || 0,
39548                   areaCodes: c[4] || null
39549               };
39550             }
39551             
39552             var cfg = {
39553                 cls: 'form-group',
39554                 cn: []
39555             };
39556             
39557             var input =  {
39558                 tag: 'input',
39559                 id : id,
39560                 cls : 'form-control tel-input',
39561                 autocomplete: 'new-password'
39562             };
39563             
39564             var hiddenInput = {
39565                 tag: 'input',
39566                 type: 'hidden',
39567                 cls: 'hidden-tel-input'
39568             };
39569             
39570             if (this.name) {
39571                 hiddenInput.name = this.name;
39572             }
39573             
39574             if (this.disabled) {
39575                 input.disabled = true;
39576             }
39577             
39578             var flag_container = {
39579                 tag: 'div',
39580                 cls: 'flag-box',
39581                 cn: [
39582                     {
39583                         tag: 'div',
39584                         cls: 'flag'
39585                     },
39586                     {
39587                         tag: 'div',
39588                         cls: 'caret'
39589                     }
39590                 ]
39591             };
39592             
39593             var box = {
39594                 tag: 'div',
39595                 cls: this.hasFeedback ? 'has-feedback' : '',
39596                 cn: [
39597                     hiddenInput,
39598                     input,
39599                     {
39600                         tag: 'input',
39601                         cls: 'dial-code-holder',
39602                         disabled: true
39603                     }
39604                 ]
39605             };
39606             
39607             var container = {
39608                 cls: 'roo-select2-container input-group',
39609                 cn: [
39610                     flag_container,
39611                     box
39612                 ]
39613             };
39614             
39615             if (this.fieldLabel.length) {
39616                 var indicator = {
39617                     tag: 'i',
39618                     tooltip: 'This field is required'
39619                 };
39620                 
39621                 var label = {
39622                     tag: 'label',
39623                     'for':  id,
39624                     cls: 'control-label',
39625                     cn: []
39626                 };
39627                 
39628                 var label_text = {
39629                     tag: 'span',
39630                     html: this.fieldLabel
39631                 };
39632                 
39633                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39634                 label.cn = [
39635                     indicator,
39636                     label_text
39637                 ];
39638                 
39639                 if(this.indicatorpos == 'right') {
39640                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39641                     label.cn = [
39642                         label_text,
39643                         indicator
39644                     ];
39645                 }
39646                 
39647                 if(align == 'left') {
39648                     container = {
39649                         tag: 'div',
39650                         cn: [
39651                             container
39652                         ]
39653                     };
39654                     
39655                     if(this.labelWidth > 12){
39656                         label.style = "width: " + this.labelWidth + 'px';
39657                     }
39658                     if(this.labelWidth < 13 && this.labelmd == 0){
39659                         this.labelmd = this.labelWidth;
39660                     }
39661                     if(this.labellg > 0){
39662                         label.cls += ' col-lg-' + this.labellg;
39663                         input.cls += ' col-lg-' + (12 - this.labellg);
39664                     }
39665                     if(this.labelmd > 0){
39666                         label.cls += ' col-md-' + this.labelmd;
39667                         container.cls += ' col-md-' + (12 - this.labelmd);
39668                     }
39669                     if(this.labelsm > 0){
39670                         label.cls += ' col-sm-' + this.labelsm;
39671                         container.cls += ' col-sm-' + (12 - this.labelsm);
39672                     }
39673                     if(this.labelxs > 0){
39674                         label.cls += ' col-xs-' + this.labelxs;
39675                         container.cls += ' col-xs-' + (12 - this.labelxs);
39676                     }
39677                 }
39678             }
39679             
39680             cfg.cn = [
39681                 label,
39682                 container
39683             ];
39684             
39685             var settings = this;
39686             
39687             ['xs','sm','md','lg'].map(function(size){
39688                 if (settings[size]) {
39689                     cfg.cls += ' col-' + size + '-' + settings[size];
39690                 }
39691             });
39692             
39693             this.store = new Roo.data.Store({
39694                 proxy : new Roo.data.MemoryProxy({}),
39695                 reader : new Roo.data.JsonReader({
39696                     fields : [
39697                         {
39698                             'name' : 'name',
39699                             'type' : 'string'
39700                         },
39701                         {
39702                             'name' : 'iso2',
39703                             'type' : 'string'
39704                         },
39705                         {
39706                             'name' : 'dialCode',
39707                             'type' : 'string'
39708                         },
39709                         {
39710                             'name' : 'priority',
39711                             'type' : 'string'
39712                         },
39713                         {
39714                             'name' : 'areaCodes',
39715                             'type' : 'string'
39716                         }
39717                     ]
39718                 })
39719             });
39720             
39721             if(!this.preferedCountries) {
39722                 this.preferedCountries = [
39723                     'hk',
39724                     'gb',
39725                     'us'
39726                 ];
39727             }
39728             
39729             var p = this.preferedCountries.reverse();
39730             
39731             if(p) {
39732                 for (var i = 0; i < p.length; i++) {
39733                     for (var j = 0; j < this.allCountries.length; j++) {
39734                         if(this.allCountries[j].iso2 == p[i]) {
39735                             var t = this.allCountries[j];
39736                             this.allCountries.splice(j,1);
39737                             this.allCountries.unshift(t);
39738                         }
39739                     } 
39740                 }
39741             }
39742             
39743             this.store.proxy.data = {
39744                 success: true,
39745                 data: this.allCountries
39746             };
39747             
39748             return cfg;
39749         },
39750         
39751         initEvents : function()
39752         {
39753             this.createList();
39754             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39755             
39756             this.indicator = this.indicatorEl();
39757             this.flag = this.flagEl();
39758             this.dialCodeHolder = this.dialCodeHolderEl();
39759             
39760             this.trigger = this.el.select('div.flag-box',true).first();
39761             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39762             
39763             var _this = this;
39764             
39765             (function(){
39766                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39767                 _this.list.setWidth(lw);
39768             }).defer(100);
39769             
39770             this.list.on('mouseover', this.onViewOver, this);
39771             this.list.on('mousemove', this.onViewMove, this);
39772             this.inputEl().on("keyup", this.onKeyUp, this);
39773             
39774             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39775
39776             this.view = new Roo.View(this.list, this.tpl, {
39777                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39778             });
39779             
39780             this.view.on('click', this.onViewClick, this);
39781             this.setValue(this.defaultDialCode);
39782         },
39783         
39784         onTriggerClick : function(e)
39785         {
39786             Roo.log('trigger click');
39787             if(this.disabled){
39788                 return;
39789             }
39790             
39791             if(this.isExpanded()){
39792                 this.collapse();
39793                 this.hasFocus = false;
39794             }else {
39795                 this.store.load({});
39796                 this.hasFocus = true;
39797                 this.expand();
39798             }
39799         },
39800         
39801         isExpanded : function()
39802         {
39803             return this.list.isVisible();
39804         },
39805         
39806         collapse : function()
39807         {
39808             if(!this.isExpanded()){
39809                 return;
39810             }
39811             this.list.hide();
39812             Roo.get(document).un('mousedown', this.collapseIf, this);
39813             Roo.get(document).un('mousewheel', this.collapseIf, this);
39814             this.fireEvent('collapse', this);
39815             this.validate();
39816         },
39817         
39818         expand : function()
39819         {
39820             Roo.log('expand');
39821
39822             if(this.isExpanded() || !this.hasFocus){
39823                 return;
39824             }
39825             
39826             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39827             this.list.setWidth(lw);
39828             
39829             this.list.show();
39830             this.restrictHeight();
39831             
39832             Roo.get(document).on('mousedown', this.collapseIf, this);
39833             Roo.get(document).on('mousewheel', this.collapseIf, this);
39834             
39835             this.fireEvent('expand', this);
39836         },
39837         
39838         restrictHeight : function()
39839         {
39840             this.list.alignTo(this.inputEl(), this.listAlign);
39841             this.list.alignTo(this.inputEl(), this.listAlign);
39842         },
39843         
39844         onViewOver : function(e, t)
39845         {
39846             if(this.inKeyMode){
39847                 return;
39848             }
39849             var item = this.view.findItemFromChild(t);
39850             
39851             if(item){
39852                 var index = this.view.indexOf(item);
39853                 this.select(index, false);
39854             }
39855         },
39856
39857         // private
39858         onViewClick : function(view, doFocus, el, e)
39859         {
39860             var index = this.view.getSelectedIndexes()[0];
39861             
39862             var r = this.store.getAt(index);
39863             
39864             if(r){
39865                 this.onSelect(r, index);
39866             }
39867             if(doFocus !== false && !this.blockFocus){
39868                 this.inputEl().focus();
39869             }
39870         },
39871         
39872         onViewMove : function(e, t)
39873         {
39874             this.inKeyMode = false;
39875         },
39876         
39877         select : function(index, scrollIntoView)
39878         {
39879             this.selectedIndex = index;
39880             this.view.select(index);
39881             if(scrollIntoView !== false){
39882                 var el = this.view.getNode(index);
39883                 if(el){
39884                     this.list.scrollChildIntoView(el, false);
39885                 }
39886             }
39887         },
39888         
39889         createList : function()
39890         {
39891             this.list = Roo.get(document.body).createChild({
39892                 tag: 'ul',
39893                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39894                 style: 'display:none'
39895             });
39896             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39897         },
39898         
39899         collapseIf : function(e)
39900         {
39901             var in_combo  = e.within(this.el);
39902             var in_list =  e.within(this.list);
39903             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39904             
39905             if (in_combo || in_list || is_list) {
39906                 return;
39907             }
39908             this.collapse();
39909         },
39910         
39911         onSelect : function(record, index)
39912         {
39913             if(this.fireEvent('beforeselect', this, record, index) !== false){
39914                 
39915                 this.setFlagClass(record.data.iso2);
39916                 this.setDialCode(record.data.dialCode);
39917                 this.hasFocus = false;
39918                 this.collapse();
39919                 this.fireEvent('select', this, record, index);
39920             }
39921         },
39922         
39923         flagEl : function()
39924         {
39925             var flag = this.el.select('div.flag',true).first();
39926             if(!flag){
39927                 return false;
39928             }
39929             return flag;
39930         },
39931         
39932         dialCodeHolderEl : function()
39933         {
39934             var d = this.el.select('input.dial-code-holder',true).first();
39935             if(!d){
39936                 return false;
39937             }
39938             return d;
39939         },
39940         
39941         setDialCode : function(v)
39942         {
39943             this.dialCodeHolder.dom.value = '+'+v;
39944         },
39945         
39946         setFlagClass : function(n)
39947         {
39948             this.flag.dom.className = 'flag '+n;
39949         },
39950         
39951         getValue : function()
39952         {
39953             var v = this.inputEl().getValue();
39954             if(this.dialCodeHolder) {
39955                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39956             }
39957             return v;
39958         },
39959         
39960         setValue : function(v)
39961         {
39962             var d = this.getDialCode(v);
39963             
39964             //invalid dial code
39965             if(v.length == 0 || !d || d.length == 0) {
39966                 if(this.rendered){
39967                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39968                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39969                 }
39970                 return;
39971             }
39972             
39973             //valid dial code
39974             this.setFlagClass(this.dialCodeMapping[d].iso2);
39975             this.setDialCode(d);
39976             this.inputEl().dom.value = v.replace('+'+d,'');
39977             this.hiddenEl().dom.value = this.getValue();
39978             
39979             this.validate();
39980         },
39981         
39982         getDialCode : function(v = '')
39983         {
39984             if (v.length == 0) {
39985                 return this.dialCodeHolder.dom.value;
39986             }
39987             
39988             var dialCode = "";
39989             if (v.charAt(0) != "+") {
39990                 return false;
39991             }
39992             var numericChars = "";
39993             for (var i = 1; i < v.length; i++) {
39994               var c = v.charAt(i);
39995               if (!isNaN(c)) {
39996                 numericChars += c;
39997                 if (this.dialCodeMapping[numericChars]) {
39998                   dialCode = v.substr(1, i);
39999                 }
40000                 if (numericChars.length == 4) {
40001                   break;
40002                 }
40003               }
40004             }
40005             return dialCode;
40006         },
40007         
40008         reset : function()
40009         {
40010             this.setValue(this.defaultDialCode);
40011             this.validate();
40012         },
40013         
40014         hiddenEl : function()
40015         {
40016             return this.el.select('input.hidden-tel-input',true).first();
40017         },
40018         
40019         onKeyUp : function(e){
40020             
40021             var k = e.getKey();
40022             var c = e.getCharCode();
40023             
40024             if(
40025                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40026                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40027             ){
40028                 e.stopEvent();
40029             }
40030             
40031             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40032             //     return;
40033             // }
40034             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40035                 e.stopEvent();
40036             }
40037             
40038             this.setValue(this.getValue());
40039         }
40040         
40041 });
40042 /**
40043  * @class Roo.bootstrap.MoneyField
40044  * @extends Roo.bootstrap.ComboBox
40045  * Bootstrap MoneyField class
40046  * 
40047  * @constructor
40048  * Create a new MoneyField.
40049  * @param {Object} config Configuration options
40050  */
40051
40052 Roo.bootstrap.MoneyField = function(config) {
40053     
40054     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40055     
40056 };
40057
40058 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40059     
40060     /**
40061      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40062      */
40063     allowDecimals : true,
40064     /**
40065      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40066      */
40067     decimalSeparator : ".",
40068     /**
40069      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40070      */
40071     decimalPrecision : 0,
40072     /**
40073      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40074      */
40075     allowNegative : true,
40076     /**
40077      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40078      */
40079     minValue : Number.NEGATIVE_INFINITY,
40080     /**
40081      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40082      */
40083     maxValue : Number.MAX_VALUE,
40084     /**
40085      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40086      */
40087     minText : "The minimum value for this field is {0}",
40088     /**
40089      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40090      */
40091     maxText : "The maximum value for this field is {0}",
40092     /**
40093      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40094      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40095      */
40096     nanText : "{0} is not a valid number",
40097     /**
40098      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40099      */
40100     castInt : true,
40101     /**
40102      * @cfg {String} defaults currency of the MoneyField
40103      * value should be in lkey
40104      */
40105     defaultCurrency : false,
40106     /**
40107      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40108      */
40109     thousandsDelimiter : false,
40110     
40111     
40112     inputlg : 9,
40113     inputmd : 9,
40114     inputsm : 9,
40115     inputxs : 6,
40116     
40117     store : false,
40118     
40119     getAutoCreate : function()
40120     {
40121         var align = this.labelAlign || this.parentLabelAlign();
40122         
40123         var id = Roo.id();
40124
40125         var cfg = {
40126             cls: 'form-group',
40127             cn: []
40128         };
40129
40130         var input =  {
40131             tag: 'input',
40132             id : id,
40133             cls : 'form-control roo-money-amount-input',
40134             autocomplete: 'new-password'
40135         };
40136         
40137         var hiddenInput = {
40138             tag: 'input',
40139             type: 'hidden',
40140             id: Roo.id(),
40141             cls: 'hidden-number-input'
40142         };
40143         
40144         if (this.name) {
40145             hiddenInput.name = this.name;
40146         }
40147
40148         if (this.disabled) {
40149             input.disabled = true;
40150         }
40151
40152         var clg = 12 - this.inputlg;
40153         var cmd = 12 - this.inputmd;
40154         var csm = 12 - this.inputsm;
40155         var cxs = 12 - this.inputxs;
40156         
40157         var container = {
40158             tag : 'div',
40159             cls : 'row roo-money-field',
40160             cn : [
40161                 {
40162                     tag : 'div',
40163                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40164                     cn : [
40165                         {
40166                             tag : 'div',
40167                             cls: 'roo-select2-container input-group',
40168                             cn: [
40169                                 {
40170                                     tag : 'input',
40171                                     cls : 'form-control roo-money-currency-input',
40172                                     autocomplete: 'new-password',
40173                                     readOnly : 1,
40174                                     name : this.currencyName
40175                                 },
40176                                 {
40177                                     tag :'span',
40178                                     cls : 'input-group-addon',
40179                                     cn : [
40180                                         {
40181                                             tag: 'span',
40182                                             cls: 'caret'
40183                                         }
40184                                     ]
40185                                 }
40186                             ]
40187                         }
40188                     ]
40189                 },
40190                 {
40191                     tag : 'div',
40192                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40193                     cn : [
40194                         {
40195                             tag: 'div',
40196                             cls: this.hasFeedback ? 'has-feedback' : '',
40197                             cn: [
40198                                 input
40199                             ]
40200                         }
40201                     ]
40202                 }
40203             ]
40204             
40205         };
40206         
40207         if (this.fieldLabel.length) {
40208             var indicator = {
40209                 tag: 'i',
40210                 tooltip: 'This field is required'
40211             };
40212
40213             var label = {
40214                 tag: 'label',
40215                 'for':  id,
40216                 cls: 'control-label',
40217                 cn: []
40218             };
40219
40220             var label_text = {
40221                 tag: 'span',
40222                 html: this.fieldLabel
40223             };
40224
40225             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40226             label.cn = [
40227                 indicator,
40228                 label_text
40229             ];
40230
40231             if(this.indicatorpos == 'right') {
40232                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40233                 label.cn = [
40234                     label_text,
40235                     indicator
40236                 ];
40237             }
40238
40239             if(align == 'left') {
40240                 container = {
40241                     tag: 'div',
40242                     cn: [
40243                         container
40244                     ]
40245                 };
40246
40247                 if(this.labelWidth > 12){
40248                     label.style = "width: " + this.labelWidth + 'px';
40249                 }
40250                 if(this.labelWidth < 13 && this.labelmd == 0){
40251                     this.labelmd = this.labelWidth;
40252                 }
40253                 if(this.labellg > 0){
40254                     label.cls += ' col-lg-' + this.labellg;
40255                     input.cls += ' col-lg-' + (12 - this.labellg);
40256                 }
40257                 if(this.labelmd > 0){
40258                     label.cls += ' col-md-' + this.labelmd;
40259                     container.cls += ' col-md-' + (12 - this.labelmd);
40260                 }
40261                 if(this.labelsm > 0){
40262                     label.cls += ' col-sm-' + this.labelsm;
40263                     container.cls += ' col-sm-' + (12 - this.labelsm);
40264                 }
40265                 if(this.labelxs > 0){
40266                     label.cls += ' col-xs-' + this.labelxs;
40267                     container.cls += ' col-xs-' + (12 - this.labelxs);
40268                 }
40269             }
40270         }
40271
40272         cfg.cn = [
40273             label,
40274             container,
40275             hiddenInput
40276         ];
40277         
40278         var settings = this;
40279
40280         ['xs','sm','md','lg'].map(function(size){
40281             if (settings[size]) {
40282                 cfg.cls += ' col-' + size + '-' + settings[size];
40283             }
40284         });
40285         
40286         return cfg;
40287     },
40288     
40289     initEvents : function()
40290     {
40291         this.indicator = this.indicatorEl();
40292         
40293         this.initCurrencyEvent();
40294         
40295         this.initNumberEvent();
40296     },
40297     
40298     initCurrencyEvent : function()
40299     {
40300         if (!this.store) {
40301             throw "can not find store for combo";
40302         }
40303         
40304         this.store = Roo.factory(this.store, Roo.data);
40305         this.store.parent = this;
40306         
40307         this.createList();
40308         
40309         this.triggerEl = this.el.select('.input-group-addon', true).first();
40310         
40311         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40312         
40313         var _this = this;
40314         
40315         (function(){
40316             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40317             _this.list.setWidth(lw);
40318         }).defer(100);
40319         
40320         this.list.on('mouseover', this.onViewOver, this);
40321         this.list.on('mousemove', this.onViewMove, this);
40322         this.list.on('scroll', this.onViewScroll, this);
40323         
40324         if(!this.tpl){
40325             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40326         }
40327         
40328         this.view = new Roo.View(this.list, this.tpl, {
40329             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40330         });
40331         
40332         this.view.on('click', this.onViewClick, this);
40333         
40334         this.store.on('beforeload', this.onBeforeLoad, this);
40335         this.store.on('load', this.onLoad, this);
40336         this.store.on('loadexception', this.onLoadException, this);
40337         
40338         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40339             "up" : function(e){
40340                 this.inKeyMode = true;
40341                 this.selectPrev();
40342             },
40343
40344             "down" : function(e){
40345                 if(!this.isExpanded()){
40346                     this.onTriggerClick();
40347                 }else{
40348                     this.inKeyMode = true;
40349                     this.selectNext();
40350                 }
40351             },
40352
40353             "enter" : function(e){
40354                 this.collapse();
40355                 
40356                 if(this.fireEvent("specialkey", this, e)){
40357                     this.onViewClick(false);
40358                 }
40359                 
40360                 return true;
40361             },
40362
40363             "esc" : function(e){
40364                 this.collapse();
40365             },
40366
40367             "tab" : function(e){
40368                 this.collapse();
40369                 
40370                 if(this.fireEvent("specialkey", this, e)){
40371                     this.onViewClick(false);
40372                 }
40373                 
40374                 return true;
40375             },
40376
40377             scope : this,
40378
40379             doRelay : function(foo, bar, hname){
40380                 if(hname == 'down' || this.scope.isExpanded()){
40381                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40382                 }
40383                 return true;
40384             },
40385
40386             forceKeyDown: true
40387         });
40388         
40389         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40390         
40391     },
40392     
40393     initNumberEvent : function(e)
40394     {
40395         this.inputEl().on("keydown" , this.fireKey,  this);
40396         this.inputEl().on("focus", this.onFocus,  this);
40397         this.inputEl().on("blur", this.onBlur,  this);
40398         
40399         this.inputEl().relayEvent('keyup', this);
40400         
40401         if(this.indicator){
40402             this.indicator.addClass('invisible');
40403         }
40404  
40405         this.originalValue = this.getValue();
40406         
40407         if(this.validationEvent == 'keyup'){
40408             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40409             this.inputEl().on('keyup', this.filterValidation, this);
40410         }
40411         else if(this.validationEvent !== false){
40412             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40413         }
40414         
40415         if(this.selectOnFocus){
40416             this.on("focus", this.preFocus, this);
40417             
40418         }
40419         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40420             this.inputEl().on("keypress", this.filterKeys, this);
40421         } else {
40422             this.inputEl().relayEvent('keypress', this);
40423         }
40424         
40425         var allowed = "0123456789";
40426         
40427         if(this.allowDecimals){
40428             allowed += this.decimalSeparator;
40429         }
40430         
40431         if(this.allowNegative){
40432             allowed += "-";
40433         }
40434         
40435         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40436         
40437         var keyPress = function(e){
40438             
40439             var k = e.getKey();
40440             
40441             var c = e.getCharCode();
40442             
40443             if(
40444                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40445                     allowed.indexOf(String.fromCharCode(c)) === -1
40446             ){
40447                 e.stopEvent();
40448                 return;
40449             }
40450             
40451             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40452                 return;
40453             }
40454             
40455             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40456                 e.stopEvent();
40457             }
40458         };
40459         
40460         this.inputEl().on("keypress", keyPress, this);
40461         
40462     },
40463     
40464     onTriggerClick : function(e)
40465     {   
40466         if(this.disabled){
40467             return;
40468         }
40469         
40470         this.page = 0;
40471         this.loadNext = false;
40472         
40473         if(this.isExpanded()){
40474             this.collapse();
40475             return;
40476         }
40477         
40478         this.hasFocus = true;
40479         
40480         if(this.triggerAction == 'all') {
40481             this.doQuery(this.allQuery, true);
40482             return;
40483         }
40484         
40485         this.doQuery(this.getRawValue());
40486     },
40487     
40488     getCurrency : function()
40489     {   
40490         var v = this.currencyEl().getValue();
40491         
40492         return v;
40493     },
40494     
40495     restrictHeight : function()
40496     {
40497         this.list.alignTo(this.currencyEl(), this.listAlign);
40498         this.list.alignTo(this.currencyEl(), this.listAlign);
40499     },
40500     
40501     onViewClick : function(view, doFocus, el, e)
40502     {
40503         var index = this.view.getSelectedIndexes()[0];
40504         
40505         var r = this.store.getAt(index);
40506         
40507         if(r){
40508             this.onSelect(r, index);
40509         }
40510     },
40511     
40512     onSelect : function(record, index){
40513         
40514         if(this.fireEvent('beforeselect', this, record, index) !== false){
40515         
40516             this.setFromCurrencyData(index > -1 ? record.data : false);
40517             
40518             this.collapse();
40519             
40520             this.fireEvent('select', this, record, index);
40521         }
40522     },
40523     
40524     setFromCurrencyData : function(o)
40525     {
40526         var currency = '';
40527         
40528         this.lastCurrency = o;
40529         
40530         if (this.currencyField) {
40531             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40532         } else {
40533             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40534         }
40535         
40536         this.lastSelectionText = currency;
40537         
40538         //setting default currency
40539         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40540             this.setCurrency(this.defaultCurrency);
40541             return;
40542         }
40543         
40544         this.setCurrency(currency);
40545     },
40546     
40547     setFromData : function(o)
40548     {
40549         var c = {};
40550         
40551         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40552         
40553         this.setFromCurrencyData(c);
40554         
40555         var value = '';
40556         
40557         if (this.name) {
40558             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40559         } else {
40560             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40561         }
40562         
40563         this.setValue(value);
40564         
40565     },
40566     
40567     setCurrency : function(v)
40568     {   
40569         this.currencyValue = v;
40570         
40571         if(this.rendered){
40572             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40573             this.validate();
40574         }
40575     },
40576     
40577     setValue : function(v)
40578     {
40579         v = this.fixPrecision(v);
40580         
40581         v = String(v).replace(".", this.decimalSeparator);
40582         
40583         this.value = v;
40584         
40585         if(this.rendered){
40586             
40587             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40588             
40589             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
40590                 this.thousandsDelimiter || ','
40591             );
40592             
40593             if(this.allowBlank && !v) {
40594                 this.inputEl().dom.value = '';
40595             }
40596             
40597             this.validate();
40598         }
40599     },
40600     
40601     getRawValue : function()
40602     {
40603         var v = this.inputEl().getValue();
40604         
40605         return v;
40606     },
40607     
40608     getValue : function()
40609     {
40610         return this.fixPrecision(this.parseValue(this.getRawValue()));
40611     },
40612     
40613     parseValue : function(value)
40614     {
40615         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40616         return isNaN(value) ? '' : value;
40617     },
40618     
40619     fixPrecision : function(value)
40620     {
40621         var nan = isNaN(value);
40622         
40623         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40624             return nan ? '' : value;
40625         }
40626         
40627         return parseFloat(value).toFixed(this.decimalPrecision);
40628     },
40629     
40630     decimalPrecisionFcn : function(v)
40631     {
40632         return Math.floor(v);
40633     },
40634     
40635     validateValue : function(value)
40636     {
40637         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40638             return false;
40639         }
40640         
40641         var num = this.parseValue(value);
40642         
40643         if(isNaN(num)){
40644             this.markInvalid(String.format(this.nanText, value));
40645             return false;
40646         }
40647         
40648         if(num < this.minValue){
40649             this.markInvalid(String.format(this.minText, this.minValue));
40650             return false;
40651         }
40652         
40653         if(num > this.maxValue){
40654             this.markInvalid(String.format(this.maxText, this.maxValue));
40655             return false;
40656         }
40657         
40658         return true;
40659     },
40660     
40661     validate : function()
40662     {
40663         if(this.disabled || this.allowBlank){
40664             this.markValid();
40665             return true;
40666         }
40667         
40668         var currency = this.getCurrency();
40669         
40670         if(this.validateValue(this.getRawValue()) && currency.length){
40671             this.markValid();
40672             return true;
40673         }
40674         
40675         this.markInvalid();
40676         return false;
40677     },
40678     
40679     getName: function()
40680     {
40681         return this.name;
40682     },
40683     
40684     beforeBlur : function()
40685     {
40686         if(!this.castInt){
40687             return;
40688         }
40689         
40690         var v = this.parseValue(this.getRawValue());
40691         
40692         if(v || v == 0){
40693             this.setValue(v);
40694         }
40695     },
40696     
40697     onBlur : function()
40698     {
40699         this.beforeBlur();
40700         
40701         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40702             //this.el.removeClass(this.focusClass);
40703         }
40704         
40705         this.hasFocus = false;
40706         
40707         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40708             this.validate();
40709         }
40710         
40711         var v = this.getValue();
40712         
40713         if(String(v) !== String(this.startValue)){
40714             this.fireEvent('change', this, v, this.startValue);
40715         }
40716         
40717         this.fireEvent("blur", this);
40718     },
40719     
40720     inputEl : function()
40721     {
40722         return this.el.select('.roo-money-amount-input', true).first();
40723     },
40724     
40725     currencyEl : function()
40726     {
40727         return this.el.select('.roo-money-currency-input', true).first();
40728     },
40729     
40730     hiddenEl : function()
40731     {
40732         return this.el.select('input.hidden-number-input',true).first();
40733     }
40734     
40735 });